What are some effective strategies for achieving comprehensive test coverage when unit testing NestJs http requests?

I am facing an issue with testing my NestJs Service. I have a method that performs a GET http request:

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)
    );
  }

I am trying to write a unit test for this method in order to cover all lines of code. Despite using the "nock" package to mock the http request, I am unable to improve the coverage result.

return throwError(error);

map(response => response.data);

These two lines remain uncovered.

Below is my test file:

describe('getEntries method', () => {
    it('should perform a 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 an error if the request fails', () => {
      nock('http://localhost:3000')
        .get('/v1/entries')
        .replyWithError('request failed');
      service.getEntries().subscribe(res => {
        expect(res).toBeUndefined();
      }, err => {
        expect(err).toBe('request failed');
      })
    });
  });

Answer №1

Although I have not previously utilized nock, a simple way to specify the response for HttpService using jest.spyOn. It is essential to remember that the return value is synchronous since it returns an observable. To test the positive scenario, you can try something like this:

it('should perform the request and retrieve 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();
    }
});

Similarly, for handling errors, you can utilize the throwError() operator.

Both of and throwError are imported from rxjs. One thing worth noting is that in this approach, you need to access the HttpService from the current module context, similar to obtaining any other service. This distinction is important to highlight.

Answer №2

Appreciate your response! The code is working at its core, but I had to tweak a few things for the testing phase. Check out the adjustments I made:

describe('getEntries method', () => {
    it('fetches entries through a get request and returns them', (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('returns an error if the request fails', (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();
        }
      });
    });
  });

I included 'status', 'statusText', 'header', 'config' while mocking AxiosResponse on httpSpy mockReturnValue to avoid any type errors.

In the second part, I initially spied on the httpService like this:

let httpService: HttpService;
httpService = module.get(HttpService)

Turns out that approach wasn't effective. Instead, I needed to spy on the HttpService injection within my Service.

constructor(private readonly http: HttpService) {}

So now my spy looks like: service['http']. With these adjustments, I've achieved full coverage on this http request :)

Thanks a bunch for your help! Enjoy the rest of your day!

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Can you uncover the secrets of static generator functions through espionage?

My current project involves an utility function that exposes a generator: export class Utility { // This utility function provides a generator that streams 2^n binary combinations for n variables public static *binaryCombinationGenerator(numVars: ...

Is there a way to make Typescript accept dot notation?

Is there a way to suppress the compile time error in Typescript when using dot notation to access a property that the compiler doesn't know about? Even though using bracket notation works, dot notation would be much more readable in my specific case. ...

Angular does not wait for the backend service call response in tap

Does anyone have a solution for subscribing to responses when the tap operator is used in a service? edit(status) { dataObj.val = status; // call post service with status.. this.service .update(dataObj) .pipe(takeUntil(this._n ...

Easy steps for importing node modules in TypeScript

I'm currently navigating the world of TypeScript and attempting to incorporate a module that is imported from a node module. I have chosen not to utilize webpack or any other build tools in order to maintain simplicity and clarity. Here is the struct ...

Arrange information in table format using Angular Material

I have successfully set up a component in Angular and Material. The data I need is accessible through the BitBucket status API: However, I am facing an issue with enabling column sorting for all 3 columns using default settings. Any help or guidance on th ...

NestJS: Specify the data type for @Body()

Consider the code snippet below: @Post() public async createPet(@Body() petDetails: PostPetDto): Promise<any> { } In this scenario, the type of @Bod() petDetails defaults to plain/any instead of the declared type of PostPetDto. What is the recommen ...

Retrieve an Array Containing a Mix of Objects and Functions in Typescript

Let's address the issue at hand: I spent several months working with a custom React Hook using plain JavaScript, and here is the code: import { useState } from 'react'; const useForm = (initialValues) => { const [state, setState] = ...

Find out if OpenAI's chat completion feature will trigger a function call or generate a message

In my NestJS application, I have integrated a chat feature that utilizes the openai createChatCompletion API to produce responses based on user input and send them back to the client in real-time. Now, with the introduction of function calls in the openai ...

Issue: The code is throwing an error "TypeError: Cannot read property 'push' of undefined" in the JavaScript engine "Hermes

Can anyone assist me with filtering an array of objects in a TypeScript React Native project using state to store array values and filter objects in the array? Having trouble with the following error in the mentioned method: LOG after item LOG inside ...

Ways to access the chosen value from Ionic's popover modal

I have been working on a simple Ionic 4 Angular app and I am using an Ionic popover modal. The code below shows how I open the popover modal in my application: //home.page.ts async openModal(ev: Event) { const modal = await this.popoverController.create({ ...

Capture and handle JavaScript errors within iframes specified with the srcDoc attribute

My current project involves creating a React component that can render any HTML/JavaScript content within an iframe using the srcDoc attribute. The challenge I am facing is implementing an error handling system to display a message instead of the iframe ...

Encountering SyntaxError when reaching the end of JSON input on a site hosted on Netlify/Render

I recently completed a Full-stack MERN (React + Node.js + MongoDB) application using guidance from this YouTube tutorial - https://www.youtube.com/watch?v=FcxjCPeicvU After successfully testing both the frontend and backend applications on my local machin ...

Exploring the possibilities with Rollbar and TypeScript

Struggling with Rollbar and TypeScript, their documentation is about as clear as AWS's. I'm in the process of creating a reusable package based on Rollbar, utilizing the latest TS version (currently 4.2.4). Let's delve into some code snipp ...

Encountering a snag when trying to grant notification permission in the Expo app

I am experiencing an issue with a simple code that previously asked for notification permissions. However, I am now encountering the following error: "Error: Encountered an exception while calling native method: Exception occurred while executing exp ...

Error in Typescript with a Bluebird library: The 'this' context is not compatible with the method's 'this' context

I have a collection of methods (some synchronous and some asynchronous) that I wish to process sequentially using bluebird.each. Here's a simplified example: import bluebird from 'bluebird'; const delay = (ms: number) => new Promise((res ...

The best approach to typing a FunctionComponent in React with typescript

I'm diving into the world of React and TypeScript for the first time. Could someone verify if this is the correct way to type a FunctionComponent? type ModalProps = { children: ReactElement<any>; show: boolean; modalClosed(): void; }; co ...

Error message indicating a problem with global typings in Angular 2 when using Webpack is displayed

My project is utilizing angular 2 with webpack and during the setup process, I encountered Duplicate identifier errors when running the webpack watcher: ERROR in [default] /angular/typings/globals/node/index.d.ts:370:8 Duplicate identifier 'unescape& ...

The TypeScript error message "The property 'target' is not found on type 'string' in React Native"

Currently, I am developing an input component in typescript for my mobile application. To manage the change of text in the text input, I decided to use the useState hook. Here is what I implemented: const [password, setPassword] = useState(''); A ...

How to link Array with Observable in Angular2 Typescript without using .interval()

Is it possible to achieve the same functionality without using the "interval()" method? I would like to link an array to an observable, and update the array as well as have the observable monitor the changes. If this approach is feasible, how can we inco ...

Promise from Jest not resolving

While testing my express server in Jest, I encountered an issue where all tests pass but Jest doesn't close or exit after completion. It seems like my server is closing after the test runs. To troubleshoot, I ran yarn jest --detectOpenHandles and rece ...