在 Jest 单元测试中模拟 imgur API 调用
Posted
技术标签:
【中文标题】在 Jest 单元测试中模拟 imgur API 调用【英文标题】:Mocking imgur API calls in Jest unit test 【发布时间】:2021-11-05 05:57:50 【问题描述】:如何为以下返回模拟值的上传和删除方法编写测试?
@Injectable()
export class ImgurService
private readonly IMGUR_API_URL = 'https://api.imgur.com/3/image';
private readonly IMGUR_CLIENT_ID = 'Client-ID';
constructor(private http: HttpClient)
upload(upload: string | File, type = 'base64'): Observable<ImgurResponse>
const headers = new HttpHeaders().set('Authorization', `$this.IMGUR_CLIENT_ID`);
const formData = new FormData();
formData.append('image', upload);
formData.append('name', UtilService.generateRandomString(32));
formData.append('type', type);
return this.http.post<ImgurResponse>(`$this.IMGUR_API_URL`, formData,
headers,
);
delete(id: string): Observable<ImgurResponse>
const headers = new HttpHeaders().set('Authorization', `$this.IMGUR_CLIENT_ID`);
return this.http.delete<ImgurResponse>(`$this.IMGUR_API_URL/$id`, headers );
这是我到目前为止的测试逻辑,到目前为止,它按预期运行:
import ImgurService from './imgur.service';
import TestBed from '@angular/core/testing';
import HttpClientTestingModule from '@angular/common/http/testing';
import of from 'rxjs';
import ImgurResponse from '../models/imgur';
describe('ImgurService', () =>
let service: ImgurService;
beforeEach(() =>
TestBed.configureTestingModule(
imports: [HttpClientTestingModule],
providers: [ImgurService],
);
service = TestBed.inject(ImgurService);
);
describe('upload()', () =>
it('should upload file', () =>
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,
;
jest.spyOn(service, 'upload').mockReturnValue(of(mockImgurResponse));
expect(service.upload('test')).toBeDefined();
);
);
);
但是,我的测试覆盖率非常低:
------------------|---------|----------|---------|---------|-------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
------------------|---------|----------|---------|---------|-------------------
imgur.service.ts | 52.94 | 0 | 33.33 | 46.67 | 15-27
------------------|---------|----------|---------|---------|-------------------
【问题讨论】:
您的测试覆盖率低应该不足为奇;实际的实现不参与测试。不要嘲笑你应该测试的东西。当你犯这个错误时,你可能会发现 TDD 工作流更明显,因为在你实际编写方法之前通过的测试会暴露它一点。 谢谢乔恩,我再看看。 设法弄清楚使用 HTTP 请求模拟。 【参考方案1】:我通过拦截请求并使用我们的模拟数据覆盖任何调用来解决此问题。在下面的示例中,如果 Imgur 的 API 被命中,我们希望使用我们之前定义的模拟数据来测试我们的服务。
import ImgurService from './imgur.service';
import TestBed from '@angular/core/testing';
import HttpClientTestingModule from '@angular/common/http/testing';
import Observable, of from 'rxjs';
import ImgurResponse from '../models/imgur';
import Injectable from '@angular/core';
import
HTTP_INTERCEPTORS,
HttpEvent,
HttpHandler,
HttpInterceptor,
HttpRequest,
HttpResponse,
from '@angular/common/http';
@Injectable()
export class ImgurServiceInterceptorMock implements HttpInterceptor
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,
;
intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>>
if (request.method === 'POST')
return of(new HttpResponse( status: 200, body: this.mockImgurResponse ));
if (request.method === 'DELETE')
return of(new HttpResponse( status: 200, body: this.mockImgurResponse ));
next.handle(request);
describe('ImgurService', () =>
let service: ImgurService;
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,
;
beforeEach(() =>
TestBed.configureTestingModule(
imports: [HttpClientTestingModule],
providers: [
ImgurService,
provide: HTTP_INTERCEPTORS,
useClass: ImgurServiceInterceptorMock,
multi: true,
,
],
);
service = TestBed.inject(ImgurService);
);
describe('upload()', () =>
it('should upload file', () =>
expect(
service.upload('test').subscribe((result) =>
expect(result).toEqual(mockImgurResponse);
)
);
);
);
describe('delete()', () =>
it('should delete file', () =>
expect(
service.delete('test').subscribe((result) =>
expect(result).toEqual(mockImgurResponse);
)
);
);
);
);
【讨论】:
以上是关于在 Jest 单元测试中模拟 imgur API 调用的主要内容,如果未能解决你的问题,请参考以下文章
如何模拟在使用 Jest 测试的 React 组件中进行的 API 调用
如何在单元测试期间使用 vue-test-utils 和 jest 模拟 mixin?