Nội dung

DaiPhan

DaiPhan

Full-Stack Developer

Full-stack developer passionate about modern web technologies, best practices, and sharing knowledge with the community.

Skills & Expertise

JavaScript TypeScript React Node.js DevOps
150+
Articles
50k+
Readers
4.9
Rating

Testing Strategies Unit Integration E2E Testing

Chiến lược testing toàn diện cho ứng dụng JavaScript với Jest React Testing Library và Cypress

Testing Strategies: Unit, Integration và E2E Testing

Một testing strategy hiệu quả là essential cho bất kỳ application nào. Bài viết này sẽ cover unit tests, integration tests, và E2E tests với practical examples.

Testing Pyramid

    /\
   /  \  E2E Tests (Few)
  /____\
 /      \
/________\ Integration Tests (Some)
|________| Unit Tests (Many)

Test Distribution Strategy

Unit Tests: 70% - Fast, Isolated, Cheap
Integration Tests: 20% - Medium speed, Component interaction
E2E Tests: 10% - Slow, Real user scenarios, Expensive

Unit Testing với Jest

Setup và Configuration

// jest.config.js
module.exports = {
  testEnvironment: 'jsdom',
  setupFilesAfterEnv: ['<rootDir>/src/setupTests.js'],
  moduleNameMapping: {
    '\\.(css|less|scss|sass)$': 'identity-obj-proxy',
    '\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$':
      '<rootDir>/__mocks__/fileMock.js',
  },
  collectCoverageFrom: [
    'src/**/*.{js,jsx,ts,tsx}',
    '!src/**/*.d.ts',
    '!src/index.js',
    '!src/serviceWorker.js',
  ],
  coverageThreshold: {
    global: {
      branches: 80,
      functions: 80,
      lines: 80,
      statements: 80,
    },
  },
};

// src/setupTests.js
import '@testing-library/jest-dom';
import 'jest-canvas-mock';

// Mock localStorage
const localStorageMock = {
  getItem: jest.fn(),
  setItem: jest.fn(),
  removeItem: jest.fn(),
  clear: jest.fn(),
};
global.localStorage = localStorageMock;

// Mock window.matchMedia
Object.defineProperty(window, 'matchMedia', {
  writable: true,
  value: jest.fn().mockImplementation(query => ({
    matches: false,
    media: query,
    onchange: null,
    addListener: jest.fn(),
    removeListener: jest.fn(),
    addEventListener: jest.fn(),
    removeEventListener: jest.fn(),
    dispatchEvent: jest.fn(),
  })),
});

Integration Testing

Testing Component Integration

// src/components/__tests__/UserProfile.integration.test.jsx
import { render, screen, waitFor, fireEvent } from '@/@/utils/test-utils';
import UserProfile from '@/UserProfile';
import { rest } from 'msw';
import { setupServer } from 'msw/node';

const server = setupServer(
  rest.get('/api/users/:id', (req, res, ctx) => {
    return res(
      ctx.json({
        id: req.params.id,
        name: 'John Doe',
        email: 'john@example.com',
        avatar: 'https://example.com/avatar.jpg',
        bio: 'Software developer',
      })
    );
  })
);

beforeAll(() => server.listen());
afterEach(() => server.resetHandlers());
afterAll(() => server.close());

describe('UserProfile Integration', () => {
  it('loads and displays user data', async () => {
    render(<UserProfile userId="123" />);

    expect(screen.getByText('Loading...')).toBeInTheDocument();

    await waitFor(() => {
      expect(screen.getByText('John Doe')).toBeInTheDocument();
      expect(screen.getByText('john@example.com')).toBeInTheDocument();
    });
  });
});

End-to-End Testing với Cypress

Setup và Configuration

// cypress.config.js
const { defineConfig } = require('cypress');

module.exports = defineConfig({
  e2e: {
    baseUrl: 'http://localhost:3000',
    viewportWidth: 1280,
    viewportHeight: 720,
    video: false,
    screenshotOnRunFailure: true,
    defaultCommandTimeout: 10000,
    requestTimeout: 10000,
    responseTimeout: 10000,
  },
});

E2E Test Examples

// cypress/e2e/auth.cy.js
describe('Authentication', () => {
  beforeEach(() => {
    cy.visit('/');
  });

  it('allows user to log in', () => {
    cy.get('[data-testid="login-link"]').click();
    cy.url().should('include', '/login');

    cy.get('[data-testid="email-input"]').type('user@example.com');
    cy.get('[data-testid="password-input"]').type('password123');
    cy.get('[data-testid="login-button"]').click();

    cy.url().should('eq', Cypress.config().baseUrl + '/');
    cy.get('[data-testid="user-menu"]').should('contain', 'user@example.com');
  });
});

Testing Best Practices

Test Organization

// src/utils/__tests__/validation.test.js
describe('Validation Utils', () => {
  describe('email validation', () => {
    it('validates correct email format', () => {
      expect(isValidEmail('user@example.com')).toBe(true);
    });

    it('rejects invalid email format', () => {
      expect(isValidEmail('invalid-email')).toBe(false);
    });
  });
});

Kết luận

Một testing strategy hiệu quả cần:

  • Unit Tests: Cover individual functions và components
  • Integration Tests: Test component interactions và API calls
  • E2E Tests: Verify complete user workflows
  • Continuous Integration: Automated testing on every commit
  • Performance Monitoring: Track test execution time và coverage

Bài viết này là phần đầu tiên trong series về Testing. Trong các bài tiếp theo, chúng ta sẽ explore Performance Testing và Security Testing.