KOA技术分享

专注 Koa.js 框架的编程知识分享

Koa 项目单元测试与接口测试实战

测试策略分层

一个健壮的 Koa 项目应包含三层测试:单元测试(Unit Test)验证独立函数逻辑,集成测试(Integration Test)验证模块协作,端到端测试(E2E)验证完整业务流程。本文重点介绍前两层在 Koa 项目中的落地实践。

测试工具选型

工具 用途 说明
Jest 测试框架 内置断言、Mock、覆盖率
SuperTest HTTP 测试 无需启动服务器即可测试接口
sqlite3 内存数据库 测试环境快速初始化数据

项目目录结构

koa-project/
├── src/
│   ├── app.js          # Koa 应用实例
│   ├── routes/
│   ├── services/
│   └── models/
├── tests/
│   ├── unit/           # 单元测试
│   └── integration/    # 接口测试
├── jest.config.js
└── package.json

单元测试示例

测试 Service 层的业务逻辑,不依赖 HTTP 和数据库:

// src/services/user.js
class UserService {
  static validatePassword(password) {
    return /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d).{8,}$/.test(password);
  }

  static calculateLevel(score) {
    if (score >= 10000) return 'VIP';
    if (score >= 5000) return 'Gold';
    if (score >= 1000) return 'Silver';
    return 'Normal';
  }
}

// tests/unit/user.test.js
const UserService = require('../../src/services/user');

describe('UserService', () => {
  test('密码强度校验', () => {
    expect(UserService.validatePassword('weak')).toBe(false);
    expect(UserService.validatePassword('Strong1Pass')).toBe(true);
  });

  test('用户等级计算', () => {
    expect(UserService.calculateLevel(800)).toBe('Normal');
    expect(UserService.calculateLevel(3000)).toBe('Silver');
    expect(UserService.calculateLevel(6000)).toBe('Gold');
    expect(UserService.calculateLevel(15000)).toBe('VIP');
  });
});

接口测试示例

使用 SuperTest 测试 HTTP 接口,配合内存数据库实现隔离:

// tests/integration/user.test.js
const request = require('supertest');
const app = require('../../src/app');

describe('用户接口', () => {
  test('注册用户', async () => {
    const res = await request(app.callback())
      .post('/api/users/register')
      .send({
        username: 'testuser',
        password: 'Test1234',
        email: 'test@example.com'
      });

    expect(res.status).toBe(201);
    expect(res.body).toHaveProperty('id');
    expect(res.body.username).toBe('testuser');
  });

  test('登录并获取 Token', async () => {
    const res = await request(app.callback())
      .post('/api/users/login')
      .send({
        username: 'testuser',
        password: 'Test1234'
      });

    expect(res.status).toBe(200);
    expect(res.body).toHaveProperty('token');
    global.token = res.body.token;
  });

  test('获取当前用户信息', async () => {
    const res = await request(app.callback())
      .get('/api/users/me')
      .set('Authorization', `Bearer ${global.token}`);

    expect(res.status).toBe(200);
    expect(res.body.username).toBe('testuser');
  });

  test('未授权访问应返回 401', async () => {
    const res = await request(app.callback())
      .get('/api/users/me');

    expect(res.status).toBe(401);
  });
});

Jest 配置

// jest.config.js
module.exports = {
  testEnvironment: 'node',
  coverageDirectory: 'coverage',
  collectCoverageFrom: [
    'src/**/*.js',
    '!src/app.js'
  ],
  coverageThreshold: {
    global: {
      branches: 70,
      functions: 80,
      lines: 80,
      statements: 80
    }
  },
  setupFilesAfterEnv: ['./tests/setup.js']
};

Mock 外部依赖

测试时不应调用真实的第三方服务:

// tests/unit/sms.test.js
const smsService = require('../../src/services/sms');

jest.mock('../../src/utils/smsProvider', () => ({
  send: jest.fn().mockResolvedValue({ code: 'OK', messageId: 'mock-id' })
}));

test('发送验证码', async () => {
  const result = await smsService.sendVerifyCode('13800138000');
  expect(result.success).toBe(true);
});

CI 集成

在 GitLab CI 中自动运行测试并生成覆盖率报告:

test:
  stage: test
  script:
    - npm ci
    - npm run test:coverage
  coverage: '/All files[^|]*\|[^|]*\s+([\d\.]+)/'
  artifacts:
    paths:
      - coverage/

总结

完善的测试体系是项目质量的保障。建议核心 Service 层达到 80% 以上覆盖率,接口测试覆盖所有正常和异常场景。测试代码应与业务代码同等重视,保持可读性和可维护性。

← 上一篇:Koa 集成 WebSocket 实现实时通信