NestJS Service的HTTP请求方法单元测试如何实现全覆盖?
如何实现NestJS Service中HTTP请求方法的单元测试全覆盖?
你的问题核心在于没有正确处理Observable的异步特性,以及错误场景的断言逻辑和nock的错误抛出方式不匹配,导致map和throwError的代码行没被覆盖。下面我会一步步帮你修正测试代码,实现100%的代码覆盖。
问题分析
- 成功场景测试的异步问题:你原来的测试中,
subscribe是异步执行的,但测试函数会直接结束,不会等待回调执行,导致map(response => response.data)的代码根本没机会运行,自然不会被覆盖。 - 错误场景的断言不匹配:nock的
replyWithError默认抛出的是Error对象,而非字符串,但你的断言是expect(err).toBe('request failed'),这会导致断言失败,错误回调的逻辑没被正确触发,进而throwError的代码没被覆盖。
修正后的测试代码
首先,确保你导入了RxJS的lastValueFrom(RxJS 7+推荐用这个方法获取Observable的最终值):
import { lastValueFrom } from 'rxjs';
1. 成功场景测试(覆盖map行)
用async/await配合lastValueFrom等待Observable完成,确保map操作符的代码被执行:
describe('getEntries method', () => { it('should send GET request and return entries', async () => { // 拦截目标请求,返回模拟数据 nock('http://localhost:3000') .get('/v1/entries') .reply(200, { data: require('../mocks/entries.json') }); // 等待Observable完成,获取结果 const result = await lastValueFrom(service.getEntries()); // 断言结果匹配模拟数据 expect(result).toEqual(require('../mocks/entries.json')); });
2. 错误场景测试(覆盖throwError行)
调整错误断言逻辑,匹配nock抛出的Error对象,同时用expect.rejects简化错误捕获:
it('should throw error when request fails', async () => { // 用Error对象抛出错误,和nock的默认行为一致 nock('http://localhost:3000') .get('/v1/entries') .replyWithError(new Error('request failed')); // 断言Observable会抛出指定错误 await expect(lastValueFrom(service.getEntries())).rejects.toThrow('request failed'); }); });
如果你的业务场景需要处理HTTP状态码错误(比如500、404),而不是网络错误,可以这样写测试:
it('should handle HTTP errors', async () => { nock('http://localhost:3000') .get('/v1/entries') .reply(500, { message: 'Internal Server Error' }); await expect(lastValueFrom(service.getEntries())).rejects.toMatchObject({ status: 500, message: 'Internal Server Error' }); });
额外注意事项
- 确保nock拦截的URL和实际请求的URL完全一致:检查
this.apiHost是否确实是http://localhost:3000,HREF.entries是否是/v1/entries,任何路径或主机的不匹配都会导致nock无法拦截请求。 - 如果你用的是Jest,确保测试模块正确注入了
HttpService:在测试的Test.createTestingModule中导入HttpModule,或者用jest.mock模拟HttpService(不过用nock的话,直接用真实的HttpService即可)。
内容的提问来源于stack exchange,提问作者Lukas
相关产品推荐
相关产品推荐

