Roberto Correia
Roberto Correia

Reputation: 1726

Jest with NestJS and async function

I'm trying to a test a async function of a service in nestJS.

this function is async... basically get a value (JSON) from database (using repository - TypeORM), and when successfully get the data, "transform" to a different class (DTO)... the implementation:

async getAppConfig(): Promise<ConfigAppDto> {
  return this.configRepository.findOne({
    key: Equal("APPLICATION"),
  }).then(config => {
    if (config == null) {
      return new class implements ConfigAppDto {
        clientId = '';
        clientSecret = '';
      };
    }
    return JSON.parse(config.value) as ConfigAppDto;
  });
}

using a controller, I checked that this worked ok. Now, I'm trying to use Jest to do the tests, but with no success... My problem is how to mock the findOne function from repository..

Edit: I'm trying to use @golevelup/nestjs-testing to mock Repository!

I already mocked the repository, but for some reason, the resolve is never called..

describe('getAppConfig', () => {
  const repo = createMock<Repository<Config>>();

  beforeEach(async () => {
    await Test.createTestingModule({
      providers: [
        ConfigService,
        {
          provide: getRepositoryToken(Config),
          useValue: repo,
        }
      ],
    }).compile();
  });

  it('should return ConfigApp parameters', async () => {
    const mockedConfig = new Config('APPLICATION', '{"clientId": "foo","clientSecret": "bar"}');
    repo.findOne.mockResolvedValue(mockedConfig);
    expect(await repo.findOne()).toEqual(mockedConfig); // ok

    const expectedReturn = new class implements ConfigAppDto {
      clientId = 'foo';
      clientSecret = 'bar';
    };
    expect(await service.getAppConfig()).toEqual(expectedReturn);

    // jest documentation about async -> https://jestjs.io/docs/en/asynchronous
    // return expect(service.getAppConfig()).resolves.toBe(expectedReturn);
  });
})

using debug, I see that the service.getAppConfig() is called, the repository.findOne() too, but the .then of repository of findOne is never called.

Update: I'm trying to mock the repository using @golevelup/nestjs-testing, and for some reason, the mocked result don't works on service. If I mock the repository using only jest (like code below), the test works... so, I think my real problem it's @golevelup/nestjs-testing.

...
provide: getRepositoryToken(Config),
useValue: {
  find: jest.fn().mockResolvedValue([new Config()])
},
...

Upvotes: 4

Views: 5386

Answers (3)

Adrian Castro
Adrian Castro

Reputation: 180

This answer from @roberto-correia made me wonder if there must be something wrong with the way we are using createMock from the package @golevelup/nestjs-testing.

It turns out that the reason why the method exceeds the execution time has to do with the fact that createMock does not implement the mocking, and does not return anything, unless told to do so.

To make the method work, we have to make the mocked methods resolve something at the beginning of the test:

usersRepository.findOneOrFail.mockResolvedValue({ userId: 1, email: "[email protected]" });

A basic working solution:

describe("UsersService", () => {
  let usersService: UsersService;
  const usersRepository = createMock<Repository<User>>();

  beforeEach(async () => {
    const module: TestingModule = await Test.createTestingModule({
      providers: [
        UsersService,
        {
          provide: getRepositoryToken(User),
          useValue: usersRepository,
        },
    }).compile();

    usersService = module.get(UsersService);
  });

  it("should be defined", () => {
    expect(usersService).toBeDefined();
  });
  it("finds a user", async () => {
    usersRepository.findOne.mockResolvedValue({ userId: 1, email: "[email protected]" });

    expect(await usersRepository.findOne()).toBe({ userId: 1, email: "[email protected]" });
  });
});

Upvotes: 1

Roberto Correia
Roberto Correia

Reputation: 1726

So, my real problem is how I'm mocking the Repository on NestJS. For some reason, when I mock using the @golevelup/nestjs-testing, weird things happens!

I really don't found a good documentation about this on @golevelup/nestjs-testing, so, I gave up using it.

My solution for the question was to use only Jest and NestJS functions... the result code was:

Service:

// i'm injecting Connection because I need for some transactions later;
constructor(@InjectRepository(Config) private readonly configRepo: Repository<Config>, private connection: Connection) {}

async getAppConfig(): Promise<ConfigApp> {
  return this.configRepo.findOne({
    key: Equal("APPLICATION"),
  }).then(config => {
    if (config == null) {
      return new ConfigApp();
    }
    return JSON.parse(config.value) as ConfigApp;
  })
}

Test:

describe('getAppConfig', () => {
  const configApi = new Config();
  configApi.key = 'APPLICATION';
  configApi.value = '{"clientId": "foo", "clientSecret": "bar"}';

  beforeEach(async () => {
    const module = await Test.createTestingModule({
      providers: [
        ConfigAppService,
        {
          provide: getRepositoryToken(Config),
          useValue: {
            findOne: jest.fn().mockResolvedValue(new
            Config("APPLICATION", '{"clientId": "foo", "clientSecret": "bar"}')),
          },
        },
        {
          provide: getConnectionToken(),
          useValue: {},
        }
      ],
    }).compile();

    service = module.get<ConfigAppService>(ConfigAppService);
  });

  it('should return ConfigApp parameters', async () => {
    const expectedValue: ConfigApp = new ConfigApp("foo", "bar");

    return service.getAppConfig().then(value => {
      expect(value).toEqual(expectedValue);
    })
  });
})

some sources utilized for this solution: https://github.com/jmcdo29/testing-nestjs/tree/master/apps/typeorm-sample

Upvotes: 3

Leo
Leo

Reputation: 751

I think expect(await repo.findOne()).toEqual(mockedConfig); works because you mocked it, so it returns right away. In the case of expect(await service.getAppConfig()).toEqual(expectedReturn);, you did not mock it so it is probably taking more time, thus the it function returns before the Promise resolved completely.

The comments you posted from jest documentation should do the trick if you mock the call to getAppConfig().

service.getAppConfig = jest.fn(() => Promise.resolve(someFakeValue))

or

spyOn(service, 'getAppConfig').and.mockReturnValue(Promise.resolve(fakeValue))

Upvotes: 1

Related Questions