Checking the interceptor response in NestJs testing

I created a basic interceptor that removes a specific field from a response:

import {
    CallHandler,
    ExecutionContext,
    Injectable,
    NestInterceptor,
} from '@nestjs/common';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';

export interface Response<T> {
    data: T;
}

@Injectable()
export class Transform<T> implements NestInterceptor<T, Response<T>> {
    intercept(
        context: ExecutionContext,
        next: CallHandler,
    ): Observable<Response<T>> {
        return next.handle().pipe(
            map((response) => {
                delete response?.value?.name;
                return response;
            }),
        );
    }
}

I am trying to figure out how to write a test case for this interceptor. Specifically, I want to verify if the 'name' property is removed from the response. However, when I tried the following test case, the response came back as undefined:

  it('should delete `name` from response if present', async () => {
    transformInterceptor = new TransformRepoResponse();
    const context = {
      switchToHttp: () => ({
        getRequest: () => jest.fn(),
      }),
    };
    const next = {
      handle: jest.fn().mockReturnValue({
        pipe: jest.fn(),
      }),
    };
    requestMock.method = 'GET';
    const response = await transformInterceptor.intercept(context, next);
    expect(response.value.name).toBe(undefined);
  });

Answer №1

Interceptors do not support the async keyword, so you must use lastValueFrom() as a wrapper to await their response. Additionally, ensure that your next.handle() returns an observable that can be manipulated using .pipe. Here is an example:

it('should remove `name` from response if it exists', (done) => {
    transformInterceptor = new TransformRepoResponse();
    const context = {
      switchToHttp: () => ({
        getRequest: () => jest.fn(),
      }),
    };
    const next = {
      handle: jest.fn().mockReturnValue(of({
        value: {
          name: 'name-field',
          foo: 'foo-field',
        }
      }),
    };
    requestMock.method = 'GET';
    let errors = []
    transformInterceptor.intercept(context, next).subscribe({
      next: (data) => {
        try {
          expect(data).toEqual({
            value: { foo: 'foo-field' }
          })
        } catch (err) {
          errors.push(err);
        }
      complete: () => done(errors.length ? errors.join(' ') : undefined)
      }
    })
  });

By treating the interceptor as an observable in your test, you are anticipating only one value and can use the next parameter of subscribe. This signals Jest that the test is finished when the observable completes. Any errors that occur during testing are captured and included in the done call if there are any.

Answer №2

This code snippet was effective in solving the given problem:

import { CallHandler } from '@nestjs/common';
import { lastValueFrom, of } from 'rxjs';
import { createMocks } from 'node-mocks-http';
import { ExecutionContextHost } from '@nestjs/core/helpers/execution-context-host';

describe('Testing the Transform Interceptor', () => {
  let transformInterceptor: Transform<any>;
  beforeEach(() => {
    transformInterceptor = new Transform();
  });

  it('should be defined', () => {
    expect(transformInterceptor).toBeDefined();
  });

  it('should eliminate the name attribute', async () => {
    const { req, res } = createMocks();
    const testContext = new ExecutionContextHost([req, res]);
    const nextSpy: CallHandler<any> = {
      handle: () =>
        of({ what: 'ever', value: { name: 'Mario', something: 'else' } }),
    };

    await expect(
      lastValueFrom(transformInterceptor.intercept(testContext, nextSpy)),
    ).resolves.toEqual({ what: 'ever', value: { something: 'else' } });
  });
});

I discovered a helpful hint that led me to this solution at: https://github.com/leosuncin/nest-api-example/blob/master/src/auth/interceptors/token.interceptor.spec.ts

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

sinon refuses to acknowledge a stub being called despite evidence of it being executed

While using sinon to stub a function called res.status, I encountered a scenario where the unit testing produced conflicting results. Despite ensuring that the function was being called during debugging, sinon failed and claimed it wasn't called in on ...

Personalized context hook TypeScript

I have been experimenting with a custom hook and the context API, based on an interesting approach that I found in this repository. However, I encountered an error when trying to use it with a simple state for a number. Even though I wanted to create a mo ...

Getting stuck in an endless loop while making a call to Axios for data fetch with React Suspense

I am currently working on fetching data using Axios and displaying it within a suspense element. I came across a tutorial that demonstrates this process: https://dev.to/darkmavis1980/a-practical-example-of-suspense-in-react-18-3lln However, I am encounter ...

Tips for adjusting the dimensions of a map within the app.component.html

Within the code snippet below, my aim is to adjust the width and height of the map using the style tag shown here: <style> #map3 .map { width: 100%; height:90px; } </style> Despite trying various values for width an ...

Create a new function and assign it to "this" using the button inside ngFor loop

I am working with a li tag that has a *ngFor directive: <li *ngFor="let items of buttons"> <button (click)="newMap(items.id, $event)"> {{ items.name }} </button> </li> The buttons array looks like this: buttons = [ {nam ...

What is the best way to implement forwardRef in a distinct select component?

Currently, I am utilizing react, typescript, and styled-components to work on my project. Specifically, I am aiming to create a select component for use in React Hook Form. Initially, everything seemed to be in order, but I encountered an error from typesc ...

Creating conditional keys using the Zod library based on the value of another key

Incorporating the TMDB API into my project, I am making an effort to enhance type safety by reinforcing some of the TypeScript concepts I am learning. To achieve this, I am utilizing Zod to define the structure of the data returned by the API. Upon invest ...

The functionality of Angular/Typescript class.name appears to fail during a production build

Using Angular 5, I encountered an unusual problem with the class.name property. We have a TypeScript function as shown below: export class ApiService { public list<T>(c: new(values: Object)=> T) { var cname = c.name; .... } } When ...

Tips for refreshing the service page in Ionic 2

One of my services is called "user-data", and its main function is to fetch "user data" when a request is received. I have a specific page that is responsible for displaying this "user data". Upon loading the page, it retrieves the data and displays it. ...

Encountering errors 'LeftSegment' not found and 'infer' not found within the react-router directory in the node_modules folder

Currently, I am in the process of updating my application from react-router v3 to v6. At the moment, I have successfully installed react-router-dom v6.2.1 as well as react-router v6.2. Additionally, since I am using Typescript, I have also installed @types ...

Is it possible to pass a Styled Components Theme as Props to a Material UI element?

After spending 9 hours scouring the internet for a solution, I am at my wit's end as nothing seems to work. Currently, I am developing a React component using TypeScript. The issue lies with a simple use of the Material UI Accordion: const Accordion ...

Attempting to simulate the behavior of Angular2 Token during testing

Currently, I am working on obtaining a token that is required for API authentication to retrieve a list. My approach begins with importing the angular2-token library: import { Angular2TokenService } from 'angular2-token'; After this, I include ...

Troubleshooting vague errors with uploading large files in Golang's net/http protocol

I've encountered a challenging error while uploading large files to a server built with Golang's default net/http package. The upload process is defined as follows: uploadForm.onsubmit = () => { const formData = new FormData(uploa ...

Mocking a function globally in React using Jest/Enzyme

At times, I encounter situations where I need to test functions that are imported into a React component using jest/enzyme. Typically, I can access a function within a component using wrapper.instance().functionName and then verify if the function has bee ...

Ongoing state configuration in a React hook

My custom hook: export function useToken2() { const { data: session, status } = useSession(); const [token, setToken] = useState<string | null>(null); useEffect(() => { if (status === 'authenticated' && session?.accessToken) { ...

The type 'ReadableStream<any>' cannot be assigned to the parameter type 'ReadableStream'

Is there a way to convert a Blob into a Readable format? import {Readable} from 'stream'; const data: Blob = new Blob( ); const myReadable: Readable = (new Readable()).wrap(data.stream()); myReadable.pipe(ext); Encountering an error: ERROR in s ...

Ways to improve the feedback for Typescript when dealing with the potential existence of a nested method

Recently encountered a critical bug that I believe could have been identified with the right TypeScript setup. Struggling to come up with a suitable title, so please bear with me. While initializing a widget app, similar to a chat app loaded by a parent a ...

Update 2020: The data pathway ".builders['app-shell']" must include the mandatory property 'class'

Having exhausted all options in various forums, including StackOverflow, without success. Tried and failed: npm uninstall @angular-devkit/build-angular npm cache clean -f npm install @angular-devkit/build-angular Deleted the node_modules folder and ...

How to extract Response body as either plain text or XML in an AngularJS 2 HTTP GET call

I have a challenge with making a get request to the server that returns XML: let responseText = ""; this.http.get('http://example.com', {headers : headers}) .map((res:Response) => res.text()).subscribe(data => responseText = data); H ...

Mapping an array of objects using dynamically generated column names

If I have an array of objects containing country, state, city data, how can I utilize the .map method to retrieve unique countries, states, or cities based on specific criteria? How would I create a method that accepts a column name and maps it to return ...