Skip to content

自动化测试实践指南

简介

自动化测试是现代软件开发中保证代码质量的重要手段。本文将介绍单元测试、集成测试和端到端测试的实践方法。

单元测试

1. Jest 配置

javascript
// jest.config.js
module.exports = {
  preset: 'ts-jest',
  testEnvironment: 'jsdom',
  moduleNameMapper: {
    '^@/(.*)$': '<rootDir>/src/$1'
  },
  setupFilesAfterEnv: ['<rootDir>/jest.setup.js'],
  collectCoverageFrom: [
    'src/**/*.{js,jsx,ts,tsx}',
    '!src/**/*.d.ts'
  ]
};

2. 测试用例

javascript
// math.test.js
import { add, subtract } from './math';

describe('Math functions', () => {
  test('adds 1 + 2 to equal 3', () => {
    expect(add(1, 2)).toBe(3);
  });

  test('subtracts 5 - 3 to equal 2', () => {
    expect(subtract(5, 3)).toBe(2);
  });
});

// component.test.js
import { mount } from '@vue/test-utils';
import Counter from './Counter.vue';

describe('Counter.vue', () => {
  test('increments count when button is clicked', async () => {
    const wrapper = mount(Counter);
    await wrapper.find('button').trigger('click');
    expect(wrapper.find('.count').text()).toBe('1');
  });
});

集成测试

1. API 测试

javascript
// api.test.js
import axios from 'axios';
import MockAdapter from 'axios-mock-adapter';

describe('API Integration', () => {
  let mock;

  beforeEach(() => {
    mock = new MockAdapter(axios);
  });

  afterEach(() => {
    mock.restore();
  });

  test('fetches user data successfully', async () => {
    const userData = { id: 1, name: 'John' };
    mock.onGet('/api/user/1').reply(200, userData);

    const response = await axios.get('/api/user/1');
    expect(response.data).toEqual(userData);
  });
});

2. 组件集成测试

javascript
// integration.test.js
import { mount } from '@vue/test-utils';
import { createStore } from 'vuex';
import App from './App.vue';

describe('App Integration', () => {
  let store;
  let wrapper;

  beforeEach(() => {
    store = createStore({
      state: {
        count: 0
      },
      mutations: {
        increment(state) {
          state.count++;
        }
      }
    });

    wrapper = mount(App, {
      global: {
        plugins: [store]
      }
    });
  });

  test('updates store when incrementing', async () => {
    await wrapper.find('button').trigger('click');
    expect(store.state.count).toBe(1);
  });
});

端到端测试

1. Cypress 配置

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

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

2. E2E 测试用例

javascript
// cypress/e2e/login.cy.js
describe('Login Page', () => {
  beforeEach(() => {
    cy.visit('/login');
  });

  it('should login successfully with valid credentials', () => {
    cy.get('[data-test="username"]').type('testuser');
    cy.get('[data-test="password"]').type('password123');
    cy.get('[data-test="submit"]').click();
    cy.url().should('include', '/dashboard');
  });

  it('should show error with invalid credentials', () => {
    cy.get('[data-test="username"]').type('wronguser');
    cy.get('[data-test="password"]').type('wrongpass');
    cy.get('[data-test="submit"]').click();
    cy.get('[data-test="error"]').should('be.visible');
  });
});

测试覆盖率

1. 覆盖率配置

javascript
// jest.config.js
module.exports = {
  collectCoverage: true,
  coverageThreshold: {
    global: {
      branches: 80,
      functions: 80,
      lines: 80,
      statements: 80
    }
  },
  coverageReporters: ['text', 'lcov', 'html']
};

2. 覆盖率报告

bash
# 运行测试并生成覆盖率报告
npm test -- --coverage

# 查看 HTML 报告
open coverage/lcov-report/index.html

测试最佳实践

1. 测试组织

javascript
// 测试文件结构
src/
  components/
    Button/
      Button.vue
      Button.test.js
  utils/
    math.js
    math.test.js
  __tests__/
    integration/
      api.test.js
    e2e/
      login.test.js

2. 测试数据

javascript
// test/fixtures/users.js
export const mockUsers = [
  { id: 1, name: 'John', email: 'john@example.com' },
  { id: 2, name: 'Jane', email: 'jane@example.com' }
];

// test/factories/user.js
export const createUser = (overrides = {}) => ({
  id: 1,
  name: 'Test User',
  email: 'test@example.com',
  ...overrides
});

持续集成

1. GitHub Actions

yaml
# .github/workflows/test.yml
name: Test

on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v2
    
    - name: Setup Node.js
      uses: actions/setup-node@v2
      with:
        node-version: '16'
        
    - name: Install dependencies
      run: pnpm install
      
    - name: Run tests
      run: pnpm test
      
    - name: Upload coverage
      uses: codecov/codecov-action@v2

2. 测试报告

yaml
# .github/workflows/test-report.yml
name: Test Report

on:
  workflow_run:
    workflows: ["Test"]
    types:
      - completed

jobs:
  report:
    runs-on: ubuntu-latest
    steps:
    - name: Notify Slack
      uses: 8398a7/action-slack@v3
      with:
        status: ${{ github.event.workflow_run.conclusion }}
        fields: repo,message,commit,author,action,eventName,ref,workflow
      env:
        SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}

最佳实践

  1. 测试策略

    • 遵循测试金字塔
    • 保持测试独立性
    • 使用适当的测试工具
    • 定期维护测试用例
  2. 代码质量

    • 编写可测试的代码
    • 使用测试驱动开发
    • 保持测试代码整洁
    • 及时更新测试用例
  3. 持续改进

    • 监控测试覆盖率
    • 分析测试失败原因
    • 优化测试执行时间
    • 定期审查测试策略

工具链

1. 测试工具

json
{
  "devDependencies": {
    "jest": "^27.0.0",
    "cypress": "^10.0.0",
    "@testing-library/vue": "^7.0.0",
    "@vue/test-utils": "^2.0.0",
    "axios-mock-adapter": "^1.20.0"
  }
}

2. 测试辅助工具

json
{
  "devDependencies": {
    "faker": "^5.5.0",
    "msw": "^0.40.0",
    "sinon": "^12.0.0",
    "testdouble": "^3.16.0"
  }
}

总结

自动化测试是保证软件质量的重要手段,通过合理使用测试工具和遵循最佳实践,可以构建出可靠、可维护的测试体系。

参考资源