0

SpyOn is used to detect the parameters passed to an underlying function. In some cases we want to modify the result from the call-through and return that to the caller.

How do we get the result of of the call-through in SpyOn before it is returned?

Here is an example:

import {INestApplication} from '@nestjs/common';
import {Test} from '@nestjs/testing';
import {CommonModule} from '@src/common/common.module';
import {Repository} from 'typeorm';
import {User} from '@src/database/entity/user.entity';

describe('AuthService', () => {
    let service: AuthService;
    let app: INestApplication;
    let repo: Repository<User>;

    beforeEach(async () => {
        const module = await Test.createTestingModule({
            imports: [CommonModule],
            providers: [AuthService, {provide: 'USER_REPOSITORY',
                        useValue: mockRepositoryUser()},
           ]}).compile();

        app = module.createNestApplication();
        await app.init();

        service = module.get<AuthService>(AuthService);
        repo = module.get('USER_REPOSITORY');
    });  

    it('test passthrough ', async () => {

        const sp = jest.spyOn(repo , 'findOne').mockImplementation(async (args) => {
           // here we want to first call the original FindOne function 
           // and get the result and then modify that 
           // result and return it. 

           const result  = await originalRepo.findOne(args)  <= ???     
           result.user = {name: 'david'};
           return result;
        );

        service.doWork();
        expect(sp).toHaveBeenCalledTimes(2); 
    });
});

Of course in the mockImplementation I can return any value. But let's say I don't want to statically return a value and just want to use the underlying database to get some result and then modify it. Since I have already mocked the entire store with some valid data every time I call the findOne I get valid results. But in order to test the failure cases I want to modify the returned results from the call-through.

I hope that is clear.

David Dehghan
  • 22,159
  • 10
  • 107
  • 95

1 Answers1

1

Well, it looks like you can't. The key thing to know is that jest.spyOn is just sugar for the basic jest.fn() usage. We can achieve the same goal by storing the original implementation, setting the mock implementation to to original, and re-assigning the original later:

import * as app from "./app";
import * as math from "./math";

test("calls math.add", () => {
  // store the original implementation
  const originalAdd = math.add;

  // mock add with the original implementation
  math.add = jest.fn(originalAdd);

  // spy the calls to add
  expect(app.doAdd(1, 2)).toEqual(3);
  expect(math.add).toHaveBeenCalledWith(1, 2);

  // override the implementation
  math.add.mockImplementation(() => "mock");
  expect(app.doAdd(1, 2)).toEqual("mock");
  expect(math.add).toHaveBeenCalledWith(1, 2);

  // restore the original implementation
  math.add = originalAdd;
  expect(app.doAdd(1, 2)).toEqual(3);
});

The need for this will be addressed by https://github.com/facebook/jest/issues/6180 When we can conditionally return different values from mocks.

David Dehghan
  • 22,159
  • 10
  • 107
  • 95