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

Jest单元测试中Mock Imgur API,如何为上传删除方法编写返回模拟值的测试

问题原因

你当前测试覆盖率低的核心原因有两个:

  • 你直接用jest.spyOn(service, 'upload').mockReturnValue(...)Mock了整个upload方法,导致方法内部的Header构造、FormData拼装、HttpClient调用逻辑完全没有执行,所以对应行没有被覆盖率统计到
  • 完全没有编写delete方法的测试用例

Angular的Http服务测试不应该直接Mock服务自身的方法,而是借助HttpClientTestingModule提供的HttpTestingController拦截校验Http请求,既可以验证请求参数的正确性,也能完整执行服务方法内的所有逻辑。


调整后的测试代码

import { ImgurService } from './imgur.service';
import { TestBed } from '@angular/core/testing';
import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';
import { ImgurResponse } from '../models/imgur';
// 请替换为你项目中UtilService的实际引入路径
import { UtilService } from './util.service';

describe('ImgurService', () => {
  let service: ImgurService;
  let httpTestingController: HttpTestingController;
  // 模拟随机字符串返回值,避免依赖UtilService的具体实现
  const mockRandomStr = 'test_random_string_12345678901234567890';

  beforeEach(() => {
    TestBed.configureTestingModule({
      imports: [HttpClientTestingModule],
      providers: [ImgurService],
    });
    service = TestBed.inject(ImgurService);
    httpTestingController = TestBed.inject(HttpTestingController);
    // Mock UtilService的静态随机字符串生成方法
    jest.spyOn(UtilService, 'generateRandomString').mockReturnValue(mockRandomStr);
  });

  // 每次测试结束后校验没有多余的未处理请求
  afterEach(() => {
    httpTestingController.verify();
  });

  describe('upload()', () => {
    it('should send correct upload request and return response', (done) => {
      const testUploadContent = 'test_base64_content';
      const testType = 'base64';
      const mockImgurResponse: ImgurResponse = {
        data: {
          id: 'orunSTu',
          title: null,
          description: null,
          datetime: 1587998106,
          type: 'image/png',
          animated: false,
          width: 2100,
          height: 1709,
          size: 138557,
          views: 0,
          bandwidth: 0,
          vote: null,
          favorite: false,
          nsfw: null,
          section: null,
          account_url: null,
          account_id: 0,
          is_ad: false,
          in_most_viral: false,
          tags: [],
          ad_type: 0,
          ad_url: '',
          in_gallery: false,
          deletehash: 'N9YaI4CIkq3rIar',
          name: 'Hero Image',
          link: 'https://i.imgur.com/keznKEA.png',
        },
        success: true,
        status: 200,
      };

      // 调用upload方法触发请求
      service.upload(testUploadContent, testType).subscribe(res => {
        // 校验返回值符合预期
        expect(res).toEqual(mockImgurResponse);
        done();
      });

      // 拦截匹配的POST请求
      const req = httpTestingController.expectOne('https://api.imgur.com/3/image');
      // 校验请求方法正确
      expect(req.request.method).toBe('POST');
      // 校验请求头携带正确的Authorization
      expect(req.request.headers.get('Authorization')).toBe('Client-ID');
      // 校验FormData参数正确
      const formData = req.request.body as FormData;
      expect(formData.get('image')).toBe(testUploadContent);
      expect(formData.get('name')).toBe(mockRandomStr);
      expect(formData.get('type')).toBe(testType);

      // 刷入模拟返回值结束请求
      req.flush(mockImgurResponse);
    });
  });

  describe('delete()', () => {
    it('should send correct delete request and return response', (done) => {
      const testDeleteId = 'test_img_id';
      const mockDeleteResponse: ImgurResponse = {
        data: {},
        success: true,
        status: 200
      };

      service.delete(testDeleteId).subscribe(res => {
        expect(res).toEqual(mockDeleteResponse);
        done();
      });

      // 拦截匹配的DELETE请求
      const req = httpTestingController.expectOne(`https://api.imgur.com/3/image/${testDeleteId}`);
      expect(req.request.method).toBe('DELETE');
      expect(req.request.headers.get('Authorization')).toBe('Client-ID');

      req.flush(mockDeleteResponse);
    });
  });
});

覆盖说明

调整后服务内的所有逻辑都会被完整执行:

  • upload方法内的Header构造、FormData拼装、http.post调用全链路都会被覆盖
  • delete方法的完整逻辑也会被覆盖
  • 额外新增了请求参数、请求头的正确性校验,比原有测试更符合单元测试的验证要求

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.04 23:48:00