Search code examples
nestjstypeormts-jest

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);
  });
})
  • the expect(await repo.findOne()).toEqual(mockedConfig); works great;
  • expect(await service.getAppConfig()).toEqual(expectedReturn); got a timeout => Async callback was not invoked within the 5000 ms timeout specified by jest.setTimeout;

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()])
},
...

Solution

  • 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