You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为带假后端的Angular应用搭建合理单元测试环境?

Angular单元测试:轻量模拟Restful后端方案

实现步骤

1. 准备假数据库文件

在项目的src/test/assets/mock-db/目录下创建对应业务资源的JSON文件,模拟后端数据集合:

  • 示例users.json:
[
  { "id": 1, "name": "张三" },
  { "id": 2, "name": "李四" }
]
  • 示例products.json:
[
  { "id": 1, "name": "商品A", "price": 99 }
]

2. 编写测试辅助工具

创建src/test/helpers/mock-rest-helper.ts文件,实现自动匹配Restful请求的逻辑:

import { HttpTestingController, HttpRequest } from '@angular/common/http/testing';

// 内存临时存储假数据,处理POST/PUT/DELETE的状态变化
let mockDb: Record<string, any[]> = {};

// 加载指定资源的假JSON数据到内存
async function loadMockData(resource: string): Promise<void> {
  const response = await fetch(`/base/src/test/assets/mock-db/${resource}.json`);
  mockDb[resource] = await response.json();
}

/**
 * 初始化假Restful API,自动匹配CRUD请求并返回对应假数据
 * @param httpTestingController - Angular测试用Http控制器
 * @param baseUrl - 后端API的基路径,默认/api
 */
export async function setupMockRestApi(httpTestingController: HttpTestingController, baseUrl: string = '/api'): Promise<void> {
  httpTestingController.expectOne((req: HttpRequest<any>) => {
    // 匹配Restful格式的URL:/baseUrl/resource[/id]
    const urlMatch = req.url.match(new RegExp(`${baseUrl}/([a-z]+)(/([0-9]+))?`));
    if (!urlMatch) return false;

    const [, resource, , idStr] = urlMatch;
    const id = idStr ? parseInt(idStr, 10) : null;

    // 首次请求时加载对应资源的假数据
    if (!mockDb[resource]) {
      loadMockData(resource);
    }

    // 根据HTTP方法返回对应响应
    switch (req.method) {
      case 'GET':
        req.response({
          body: id ? mockDb[resource].find(i => i.id === id) : mockDb[resource],
          status: 200
        });
        break;
      case 'POST':
        const newItem = { id: Date.now(), ...req.body };
        mockDb[resource].push(newItem);
        req.response({ body: newItem, status: 201 });
        break;
      case 'PUT':
        const updateIndex = mockDb[resource].findIndex(i => i.id === id);
        if (updateIndex !== -1) {
          mockDb[resource][updateIndex] = { ...mockDb[resource][updateIndex], ...req.body };
          req.response({ body: mockDb[resource][updateIndex], status: 200 });
        } else {
          req.response({ status: 404 });
        }
        break;
      case 'DELETE':
        mockDb[resource] = mockDb[resource].filter(i => i.id !== id);
        req.response({ status: 204 });
        break;
      default:
        req.response({ status: 405 });
    }
    return true;
  });
}

3. 在服务测试中使用

以UserService为例,编写单元测试:

import { TestBed } from '@angular/core/testing';
import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';
import { UserService } from './user.service';
import { setupMockRestApi } from '../test/helpers/mock-rest-helper';

describe('UserService', () => {
  let service: UserService;
  let httpTestingController: HttpTestingController;

  beforeEach(async () => {
    await TestBed.configureTestingModule({
      imports: [HttpClientTestingModule],
      providers: [UserService]
    }).compileComponents();

    service = TestBed.inject(UserService);
    httpTestingController = TestBed.inject(HttpTestingController);
    // 初始化假Rest API,和原服务使用的后端基URL保持一致
    await setupMockRestApi(httpTestingController, '/api');
  });

  afterEach(() => {
    // 验证所有请求都已被处理
    httpTestingController.verify();
  });

  it('should fetch all users', async () => {
    const users = await service.getUsers().toPromise();
    expect(users.length).toBe(2);
    expect(users[0].name).toBe('张三');
  });

  it('should fetch a single user by id', async () => {
    const user = await service.getUser(1).toPromise();
    expect(user.id).toBe(1);
    expect(user.name).toBe('张三');
  });

  it('should create a new user', async () => {
    const newUser = await service.createUser({ name: '王五' }).toPromise();
    expect(newUser.id).toBeTruthy();
    expect(newUser.name).toBe('王五');
  });
});

需求匹配验证

  • ✅ 无需修改原服务:原服务直接使用HttpClient,测试时通过HttpClientTestingModule拦截请求,业务代码无任何改动
  • ✅ 无复杂实现:未使用HTTP拦截器,仅依赖Angular官方测试工具+轻量辅助函数
  • ✅ 无额外配置:无需代理设置、特殊npm run脚本,执行ng test自动激活假后端
  • ✅ 假数据易维护:直接编写JSON文件,结构与真实后端返回完全一致
  • ✅ 自动适配Restful端点:辅助函数自动匹配所有CRUD操作,无需逐个定义请求匹配规则
  • ✅ 测试环境自动切换:仅在单元测试中启用,不影响开发/生产环境的真实后端调用

内容的提问来源于stack exchange,提问作者zeroquaranta

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.27 21:52:54