0

I have a simple interceptor that deletes a field from 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;
            }),
        );
    }
}

How can I write test case for this? Basically, I wanted to test if 'name' is deleted from response. I wrote the following test case but response is coming 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);
  });

2 Answers 2

2

Interceptors aren't async so you can't await their response without using lastValueFrom() as a wrapper. Also, your next.handle() should return an observable that will be acted on by the .pipe. Something like

it('should delete `name` from response if present', (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)
      }
    })
  });

Now you're testing the interceptor as an observable, you only expect one value so using the next of subscribe is fine, and you're telling Jest that the test is finished when the observable completes. You're also persisting any errors that happen by catching them in the next and using them as part of the done call if they exist.

Sign up to request clarification or add additional context in comments.

6 Comments

thanks for your answer. One more question on this, my test case does not cover the branch in the line "delete response?.value?.name;". I wrote two test cases one with no "response", and the other with response, but without "value". But still it says "branch" not covered. How do I write test case to cover all the branches in the line?
I found one issue with this solution - That the test case never fails even if: " expect(data).toEqual({ value: { foo: 'foo-field' } }) " fails. Jest marks this test case as passed. Is it because the "expect" statement is given inside "next()"??
It should report if there's a problem. You can add in error: reject to that object and see if that fixes it
Where do I add "error: reject" ? Could you please update your answer?
Sorry, was thinking about promises with observables. It'd actually be error: done or explicitly error: (err) => done(err)
|
0

This worked for me for your example:

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('Transform Interceptor test', () => {
  let transformInterceptor: Transform<any>;
  beforeEach(() => {
    transformInterceptor = new Transform();
  });

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

  it('should remove the name', 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 found good hint for this solution here: https://github.com/leosuncin/nest-api-example/blob/master/src/auth/interceptors/token.interceptor.spec.ts

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.