Kiểm thử
Tổng quan
SmartCK Hub sử dụng nhiều tầng testing:
| Loại test | Công cụ | Mục đích |
|---|---|---|
| Unit test | Vitest | Test service, repository riêng lẻ |
| Integration | Vitest + Supertest | Test API endpoint end-to-end |
| E2E | Playwright | Test giao diện trên browser |
| Component | Storybook | Phát triển và test UI component |
Backend Testing
Cấu hình Vitest
Vitest được cấu hình tại apps/backend/vitest.config.ts:
export default defineConfig({
test: {
globals: true,
environment: 'node',
include: ['src/**/*.spec.ts'],
coverage: {
provider: 'v8',
reporter: ['text', 'json', 'html'],
thresholds: {
lines: 75,
functions: 75,
branches: 75,
statements: 75,
},
},
testTimeout: 10000,
},
});
Viết Unit Test
Test file đặt cạnh file source, đuôi *.spec.ts:
services/
├── departments.service.ts
├── departments.service.spec.ts # Unit test
├── pods.service.ts
├── pods.service.spec.ts
└── pods.integration.spec.ts # Integration test
Ví dụ unit test cho service:
departments.service.spec.ts
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { DepartmentsService } from './departments.service';
describe('DepartmentsService', () => {
let service: DepartmentsService;
let mockRepository: any;
beforeEach(() => {
mockRepository = {
findAll: vi.fn(),
findById: vi.fn(),
create: vi.fn(),
};
service = new DepartmentsService(mockRepository);
});
it('should return all departments', async () => {
mockRepository.findAll.mockResolvedValue([{ id: '1', name: 'IT' }]);
const result = await service.findAll({});
expect(result).toHaveLength(1);
expect(mockRepository.findAll).toHaveBeenCalledOnce();
});
});
Test Factories
File src/test/factories.ts cung cấp factory functions để tạo test data:
import { createMockUser, createMockProject } from '@test/factories';
const user = createMockUser({ role: 'SUPER_ADMIN' });
const project = createMockProject({ status: 'active' });
Chạy Backend Tests
# Chạy toàn bộ test
pnpm test
# Chạy test của backend
cd apps/backend && pnpm test
# Chạy test với watch mode
cd apps/backend && pnpm test -- --watch
# Chạy test cụ thể
cd apps/backend && pnpm test -- departments.service
# Xem coverage report
cd apps/backend && pnpm test -- --coverage
Frontend Testing
Vitest cho Frontend
Frontend cũng dùng Vitest cho unit/integration test:
cd apps/frontend && pnpm test
Playwright E2E
Playwright test giao diện trên browser thực:
# Chạy E2E test cho menu system
pnpm test:e2e:menus
Lệnh test:e2e:menus chạy script scripts/playwright-menu-deep.cjs -- test deep navigation qua toàn bộ menu hệ thống, kiểm tra:
- Mọi menu item đều click được
- Navigation đến đúng trang
- Không có lỗi console trên mỗi trang
- Trang load thành công (không 404/500)
Storybook
Storybook dùng để phát triển và visual test UI components:
cd apps/frontend && pnpm storybook
Stories đặt tại src/stories/:
button.stories.tsx
import type { Meta, StoryObj } from '@storybook/react';
import { Button } from '@/components/ui/button';
const meta: Meta<typeof Button> = {
component: Button,
tags: ['autodocs'],
};
export default meta;
export const Primary: StoryObj<typeof Button> = {
args: { children: 'Click me', variant: 'default' },
};
export const Destructive: StoryObj<typeof Button> = {
args: { children: 'Xóa', variant: 'destructive' },
};
Quy ước đặt tên Test
| Pattern | Loại test | Ví dụ |
|---|---|---|
*.spec.ts | Unit test | departments.service.spec.ts |
*.integration.spec.ts | Integration | pods.integration.spec.ts |
*.e2e-spec.ts | E2E (backend) | auth.e2e-spec.ts |
*.test.tsx | Frontend test | button.test.tsx |
Coverage Reports
Backend coverage được output dưới 3 format:
- text: In trực tiếp ra terminal
- json: File JSON cho CI/CD
- html: Report HTML tại
apps/backend/coverage/
Mở report HTML:
# Sau khi chạy test --coverage
open apps/backend/coverage/index.html
Ngưỡng coverage tối thiểu:
| Metric | Ngưỡng |
|---|---|
| Lines | 75% |
| Functions | 75% |
| Branches | 75% |
| Statements | 75% |
CI/CD
Nếu coverage xuống dưới ngưỡng, CI pipeline sẽ fail. Đảm bảo viết test đủ trước khi tạo merge request.
Checklist trước khi Push
pnpm test # Tất cả test pass
pnpm lint # Không có linting errors
pnpm typecheck # Không có type errors
pnpm dupcheck # Không có code duplication mới