Từ Zero đến Principal Frontend Engineer (P7: Chiến lược Testing Frontend - Từ Unit Test đến E2E)

0 0 0

Người đăng: Albert Huynh

Theo Viblo Asia

Phần 1: Tại Sao Testing Là "Non-Negotiable"?

1.1 Hậu quả của Poor Testing

  • Case study: Một công ty mất $2.4 triệu do bug checkout không được phát hiện vì thiếu integration test.
  • Số liệu: 40% bug frontend đến từ state management sai (Redux, Context API) – có thể ngăn chặn bằng unit test.

1.2 Tư Duy Testing

  • Testing như documentation: Mỗi test case phải mô tả business requirement.
  • Cost of change: Chi phí sửa bug tăng gấp 10 lần nếu phát hiện muộn (theo IBM Systems Sciences Institute).

Phần 2: Chi Tiết Từng Tầng Testing Pyramid

2.1 Unit Testing: Nền Tảng Của Reliability

Công cụ tối ưu:

  • Jest (cho React/Vue) hoặc Vitest (tốc độ cực nhanh với Vite).
  • React Testing Library (khuyến khích test behavior thay vì implementation).

Ví dụ Phức Tạp: Test Custom Hook với Async Logic

// useAuthHook.js
export const useAuth = () => { const [user, setUser] = useState(null); const login = async (email, password) => { const response = await api.login(email, password); setUser(response.data.user); }; return { user, login };
}; // useAuthHook.test.js
test('login updates user state', async () => { jest.spyOn(api, 'login').mockResolvedValue({ data: { user: { id: 1, name: 'John' } } }); let hookResult; function TestComponent() { hookResult = useAuth(); return null; } render(<TestComponent />); await act(async () => { await hookResult.login('test@example.com', 'password'); }); expect(hookResult.user).toEqual({ id: 1, name: 'John' });
});

Best Practices:

  • Mocking chuẩn: Sử dụng jest.mock cho module phức tạp (ví dụ: API calls).
  • Coverage có mục tiêu: 100% cho core business logic, 70% cho UI components.

2.2 Integration Testing: Kịch Bản Thực Tế

Công cụ đề xuất:

  • MSW (Mock Service Worker): Mock API cực mạnh, hỗ trợ GraphQL/REST.
  • Testing Library render với Redux Provider.

Ví dụ: Test Form Submit với API Call và State Update

// LoginForm.test.js
import { setupServer } from 'msw/node';
import { rest } from 'msw'; const server = setupServer( rest.post('/api/login', (req, res, ctx) => { return res(ctx.json({ token: 'fake-token' })); })
); beforeAll(() => server.listen());
afterAll(() => server.close()); test('submits login form and stores token', async () => { const mockStore = configureMockStore(); const store = mockStore({}); render( <Provider store={store}> <LoginForm /> </Provider> ); fireEvent.change(screen.getByLabelText('Email'), { target: { value: 'test@example.com' } }); fireEvent.change(screen.getByLabelText('Password'), { target: { value: 'password' } }); fireEvent.click(screen.getByRole('button', { name: 'Login' })); await waitFor(() => { const actions = store.getActions(); expect(actions[0].type).toEqual('auth/loginSuccess'); expect(actions[0].payload).toEqual('fake-token'); });
});

Lỗi Thường Gặp:

  • Quá nhiều mock: Dẫn đến test không giống production. Giải pháp: Dùng MSW để mock ở network level.
  • Test quá rộng: Một test case kiểm tra cả UI + API + State. Nên tách thành nhiều test nhỏ.

2.3 E2E Testing: Tập Trung Vào Critical Paths

So Sánh Công Cụ:

Tiêu Chí Cypress Playwright
Tốc độ Chậm hơn, chạy trên browser thật Nhanh, hỗ trợ multi-browser
Hỗ trợ Mobile Giới hạn Mạnh (device emulation)
Parallel execution Cần CI setup phức tạp Hỗ trợ sẵn

Ví dụ Playwright Test cho Checkout Flow:

// checkout.spec.ts
import { test, expect } from '@playwright/test'; test('complete checkout as guest', async ({ page }) => { await page.goto('/products/1'); await page.click('text=Add to Cart'); await page.click('#cart-icon'); await page.fill('input[name="email"]', 'test@example.com'); await page.click('text=Proceed to Checkout'); await expect(page).toHaveURL('/checkout'); await page.click('text=Place Order'); await expect(page.locator('.order-confirmation')).toBeVisible();
});

Chiến Lược E2E Hiệu Quả:

  • Chỉ test happy paths: Login, Checkout, Search.
  • Dùng tagging: @smoke, @regression để phân loại test suite.
  • Kết hợp với monitoring: Sentry để bắt lỗi production chưa được test.

Phần 3: Chiến Lược Duy Trì Test Suite

3.1 Tích Hợp Vào CI/CD

# GitHub Actions example
jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - run: npm install - run: npm run test:unit -- --coverage - run: npm run test:integration - run: npx playwright test - uses: actions/upload-artifact@v3 if: failure() with: name: playwright-report path: playwright-report/

3.2 Đo Lường Hiệu Quả

  • Số liệu cần track:
    • Bug escape rate: Số bug lọt vào production / tổng bug.
    • Test flakiness: Tỉ lệ test fail không ổn định.
  • Tool: Jest Sonar reporter, Cypress Dashboard.

Kết Luận

  • Testing không phải để đạt coverage 100%, mà để giảm rủi ro business.
  • Luôn đặt câu hỏi: "Nếu test này fail, có ảnh hưởng đến user không?".

Bình luận

Bài viết tương tự

- vừa được xem lúc

Thủ thuật nhỏ để căn chỉnh image với object-fit

Chào các bạn,. Có lẽ trong hành trình code của các bạn thì không ít lần gặp vấn đề méo ảnh do fix cứng cả width, height của ảnh nhỉ? Hoặc kể cả khi bạn set value cho 1 thuộc tính weigth hoặc height còn thuộc tính còn lại để auto thì nhiều lúc ảnh cũng không được hiển thị toàn vẹn cho lắm.

0 0 56

- vừa được xem lúc

Tìm hiểu về CSS framework - Bulma

Mở đầu:. Mấy bữa nay đang lướt web thấy có giới thiệu framework bulma này, được mọi người giới thiệu gọn nhẹ và dễ sử dụng, nên mình mới tìm hiểu thử và hôm nay xin viết 1 bài viết giới thiệu sơ qua với các bạn.

0 0 40

- vừa được xem lúc

Một số mẹo vặt "hay ho" của ES6 có thể bạn chưa biết - Phần 4

Xin chào, ở 3 bài trước của series "Một số mẹo vặt "hay ho" của ES6", mình đã chia sẻ 1 số tips/tricks nhỏ với ES6, hy vọng ít nhiều nó sẽ có ích với các bạn khi áp dụng vào thực tế. Hôm nay, xin mời các bạn theo dõi phần 4 của series này.

0 0 51

- vừa được xem lúc

Tìm hiểu về Jest Mocks Test phía frontend

Giới thiệu. Chắc hẳn không ai phủ nhận rằng UnitTest là 1 phần quan trọng trong giai đoạn phát triển phần mềm, đảm bảo cho code được coverage tránh các bug không mong muốn.

0 0 42

- vừa được xem lúc

Convert từ SVG sang Icon Font như thế nào?

Chào các bạn. Như câu hỏi trên title của bài viết, hôm nay mình sẽ hướng dẫn các bạn cách convert 1 file svg 1 cách khá đơn giản và vô cùng tiện lợi cho các bạn. https://icomoon.io/app/#/select.

0 0 64

- vừa được xem lúc

Một vài thủ thuật làm việc với các dạng layout - Phần 4

. Chào mọi người, cũng đã lâu rồi mình không thấy nhau. Để tiếp tục với series's về các dạng layout hôm nay mình sẽ chia sẻ thêm một trick thú vị nữa về step layout.

0 0 50