Fixing ‘done’ Callback Issue in NestJS Testing

Updated: December 31, 2023 By: Guest Contributor Post a comment

When working on a Node.js project with NestJS, you might encounter an error related to test functions when they both take a ‘done’ callback. The error message indicates that your tests are not being constructed properly according to Jest, the testing framework commonly used with NestJS. This error arises from the fact that Jest expects tests to handle asynchronous code in a specific way, and using ‘done’ incorrectly can lead to conflicts in how tests are expected to conclude.

Understanding the ‘done’ Callback

In Jest, ‘done’ is a callback that can be used to handle asynchronous operations within tests. When passed to a test function, it tells Jest to wait for the ‘done’ to be called before considering the test as finished. However, NestJS has adopted more modern approaches, such as returning a Promise or using async/await syntax, which can cause incompatibilities when used in conjunction with the ‘done’ callback.

Sticking to Async/Await Syntax

To fix the mentioned error, one of the simplest approaches is to capitalize on JavaScript’s async/await syntax. This modern feature serves to handle asynchronous operations in a more readable and structured manner without the need for callbacks. When you define a test function as ‘async’, you can then use ‘await’ on any asynchronous operation, and Jest will know to wait for those operations to resolve before moving on to the next test.

describe('Cats Service', () => {
  let service: CatsService;

  beforeEach(async () => {
    const module: TestingModule = await Test.createTestingModule({
      providers: [CatsService],
    }).compile();

    service = module.get(CatsService);
  });

  it('should return an array of cats', async () => {
    const result = ['test cat'];
    jest.spyOn(service, 'findAll').mockImplementation(async () => result);

    expect(await service.findAll()).toBe(result);
  });
});

Avoiding the Mixture of Done and Promises

If you prefer to stick with using ‘done’, you must make sure not to mix it with other asynchronous patterns like returning Promises or using async/await. In NestJS, if you start a test with an async function, any return of a Promise will be handled properly, but adding a ‘done’ parameter to it can confuse Jest and result in the very same error. If ‘done’ is your preferred method, ensure that you do not use async/await within the same test function.

describe('Cats Service', () => {
  let service: CatsService;

  beforeAll(done => {
    Test.createTestingModule({
      providers: [CatsService],
    }).compile()
    .then(module => {
      service = module.get(CatsService);
      done();
    });
  });

  it('should find a cat by id', done => {
    service.findById(1).then(cat => {
      expect(cat.name).toBe('Whiskers');
      done();
    });
  });
});

By following these practices and ensuring consistency in handling asynchronous code, you can avoid the error concerning the ‘done’ callback in NestJS tests. Whether you opt for the elegance of async/await or the directness of the ‘done’ callback, adapting to Jest’s expectations will result in successful and error-free test suites.