Chuyển tới nội dung chính

Kiểm thử

Tổng quan

SmartCK Hub sử dụng nhiều tầng testing:

Loại testCông cụMục đích
Unit testVitestTest service, repository riêng lẻ
IntegrationVitest + SupertestTest API endpoint end-to-end
E2EPlaywrightTest giao diện trên browser
ComponentStorybookPhá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

PatternLoại testVí dụ
*.spec.tsUnit testdepartments.service.spec.ts
*.integration.spec.tsIntegrationpods.integration.spec.ts
*.e2e-spec.tsE2E (backend)auth.e2e-spec.ts
*.test.tsxFrontend testbutton.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:

MetricNgưỡng
Lines75%
Functions75%
Branches75%
Statements75%
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