Khi phát triển cùng một codebase cho React Web (Next.js) và React Native (Expo), việc đảm bảo chất lượng trên cả hai môi trường đòi hỏi một quy trình kiểm thử hệ thống, không chỉ chạy test đơn lẻ. Dưới đây là khung làm việc thực tế đã áp dụng thành công trong nhiều dự án sản xuất. 1. Phân loại test theo tầng - Unit test: Kiểm tra logic thuần túy (utils, hooks, selectors) bằng Jest. Viết test một lần, chạy trên cả hai nền tảng vì code không phụ thuộc vào runtime. - Integration test: Kết hợp component cha – con, context, router/navigation. Sử dụng React Testing Library cho web và @testing-library/react-native cho mobile. Cấu hình jest.shared.config.js để chia sẻ preset, transform, moduleNameMapper. - End‑to‑end (E2E) test: Mô phỏng hành vi người dùng thật. Trên web dùng Playwright (hỗ trợ Chromium, Firefox, WebKit). Trên mobile dùng Detox chạy trên emulator/simulator và thiết bị thật. 2. Cấu hình monorepo test - Tạo workspace scripts: `test:unit`, `test:integration`, `test:e2e:web`, `test:e2e:mobile`. - Sử dụng Turbo hoặc Nx để chạy song song, cache kết quả, chỉ chạy lại test liên quan khi code thay đổi. - Thiết lập CI (GitHub Actions) với matrix: node-version × platform (ubuntu‑latest cho web, macos‑latest cho iOS simulator, ubuntu‑latest cho Android emulator). 3. Quản lý test data và mock - Tạo thư mục `__mocks__` chung cho API client, storage (AsyncStorage ↔ localStorage), navigation, native modules. - Dùng MSW (Mock Service Worker) cho web và `msw-react-native` cho mobile để mock network request nhất quán. - Fixtures JSON đặt tại `tests/fixtures` để cả hai runner đều đọc được. 4. Đo lường độ bao phủ và gating - Bật `collectCoverageFrom` cho cả hai preset, xuất báo cáo LCOV. - Thiết lập ngưỡng tối thiểu (ví dụ 80% statements, 70% branches) trong `jest.config.js`. CI fail nếu không đạt. - Tích hợp Codecov hoặc SonarCloud để theo dõi xu hướng theo thời gian. 5. Mẹo thực chiến - Viết test theo hành vi (behavior) chứ không phải implementation detail; tránh snapshot quá nhiều. - Tách test flaky (thường là E2E) vào job riêng, cho phép retry tự động. - Sử dụng `test.each` cho các kịch bản dữ liệu đa dạng (locale, theme, quyền). - Đối với native module (camera, bluetooth) viết wrapper interface và mock interface đó thay vì mock trực tiếp module gốc. 6. Kết luận Một chiến lược test phân tầng, chia sẻ cấu hình và dữ liệu mock giúp giảm trùng lặp, phát hiện sớm regression trên cả web lẫn mobile. Đầu tư ban đầu cho CI matrix và shared config sẽ trả lại thời gian bảo trì và độ tin cậy sản phẩm trong dài hạn.