如何对 NestJs http 请求进行完全覆盖的单元测试?

Luk*_*kas 8 unit-testing typescript jestjs nestjs

我在测试我的 NestJs 服务时遇到问题。我写了一个方法,它执行 GET http 请求:

getEntries(): Observable<Entries[]> {
    Logger.log(`requesting GET: ${this.apiHost}${HREF.entries}`);
    return this.http.get(`${this.apiHost}${HREF.entries}`).pipe(
      catchError((error) => {
        return throwError(error);
      }),
      map(response => response.data)
    );
  }
Run Code Online (Sandbox Code Playgroud)

我想为这个方法编写一个单元测试。该单元测试应覆盖该方法的所有行。我尝试使用“nock”包来模拟这个http请求,但无论我如何尝试,覆盖结果总是相同的。

return throwError(error);

map(response => response.data);
Run Code Online (Sandbox Code Playgroud)

这两行被揭开了。

这是我的测试文件:

describe('getEntries method', () => {
    it('should do get request and return entries', () => {
      nock('http://localhost:3000')
        .get('/v1/entries')
        .reply(200, {
          data: require('../mocks/entries.json')
        });
      try {
        const result = service.getEntries();
        result.subscribe(res => {
          expect(res).toEqual(require('../mocks/entries.json'));
        });
      } catch (e) {
        expect(e).toBeUndefined();
      }
    });
    it('should return error if request failed', () => {
      nock('http://localhost:3000')
        .get('/v1/entries')
        .replyWithError('request failed');
      service.getEntries().subscribe(res => {
        expect(res).toBeUndefined();
      }, err => {
        expect(err).toBe('request failed');
      })
    });
  });
Run Code Online (Sandbox Code Playgroud)

Jay*_*iel 7

nock我过去没有使用过,但你可以告诉HttpService使用时要响应什么jest.spyOn。最要记住的是返回是“同步的”,因为它是可观察的返回。为了测试你的阳性案例,你可以这样做

it('should do the request and get the entries', (done) => {
  const httpSpy = jest.spyOn(httpService, 'get')
    .mockReturnValue(of({data: require('../mocks/entries.json')}))
  let data = {};
  service.getEntires().subscribe({
    next: (val) => {data = val},
    error: (err) => { throw error; }
    complete: () => {
      expect(data).toEqual(require('../mocks/entries.json'))
      done();
    }
});
Run Code Online (Sandbox Code Playgroud)

同样,对于错误路径,您可以使用throwError()运算符。

of都是throwError从 导入的rxjs。唯一需要注意的是,使用此方法,您确实需要从当前模块上下文中获取HttpService,类似于获取任何其他服务的方式。只是要确保把这一点说出来。


Luk*_*kas 5

感谢你的回复。该代码从根本上起作用。我必须进行一些更改才能使测试正常进行。这是我的改变:

describe('getEntries method', () => {
    it('should do get request and return entries', (done) => {
      jest.spyOn(service['http'], 'get').mockReturnValue(of({data: require('../mocks/entries.json'), status: 200, statusText: 'OK', headers: {}, config: {}}));
      let data = {};

      service.getEntries().subscribe({
        next: (val) => {data = val},
        error: (err) => { throw err; },
        complete: () => {
          expect(data).toEqual(require('../mocks/entries.json'))
          done();
        }
      });
    });
    it('should return error if request failed', (done) => {
      jest.spyOn(service['http'], 'get').mockReturnValue(throwError('request failed'));
      let data = {};

      service.getEntries().subscribe({
        next: (val) => {data = val},
        error: (err) => {
          expect(err).toBe('request failed');
          done();
        },
        complete: () => {
          expect(data).toBeUndefined();
          done();
        }
      });
    });
  });
Run Code Online (Sandbox Code Playgroud)

你必须在httpSpy的mockReturnValue上模拟AxiosReponse,所以我添加了'status','statusText','header','config'。否则你会得到一个类型错误。

还有第二部分。我像这样监视 httpService:

let httpService: HttpService;
httpService = module.get(HttpService)
Run Code Online (Sandbox Code Playgroud)

这是行不通的。我必须监视我的服务的 HttpService 注入。

constructor(private readonly http: HttpService) {}
Run Code Online (Sandbox Code Playgroud)

这就是为什么我的间谍看起来像:service['http']。

现在我已经完全覆盖了这个 http 请求:)

非常感谢;)祝你有美好的一天!