Test functions cannot both take a 'done' callback
Asked Answered
L

9

38

I'm trying to create a simple test with nestjs, and I'm getting this error

Test functions cannot both take a 'done' callback and return something. Either use a 'done' callback, or return a promise.

Returned value: Promise {}

The unit test is so simple, but I get an error when I use done();

it('throws an error if a user signs up with an email that is in use', async (done) => {
fakeUsersService.find = () => Promise.resolve([{ id: 1, email: 'a', password: '1' } as User]);
try {
  await service.signup('[email protected]', 'asdf');
} catch (err) {
  done();
}
});
Linnlinnaeus answered 27/1, 2022 at 18:58 Comment(1)
jestjs.io/docs/asynchronousSidoney
S
39

You are combining Async/Await and Done.

Either use asnyc/await, or done.

it('throws an error if user signs up with email that is in use', async () => {
    try {
        await service();
        expect(...);
    } catch (err) {
    }
});

or use the done format

it('throws an error if user signs up with email that is in use', (done) => {
    ...
    service()
     .then( ...) {}
     .catch( ...) {}
    }
    done();
});
Swampy answered 27/1, 2022 at 21:11 Comment(2)
this answer is misleading, its about jest v27 & not using jasmine2.Nikianikita
which is the config most people in the future will have, right? @ToKraDefeatism
T
16

for the last version from jest, you can't use `async/await , promise and done together.

the solution is

 it("throws an error if user sings up with email that is in use", async () => {
    fakeUsersService.find = () =>
      Promise.resolve([{ id: 1, email: "a", password: "1" } as User]);
    await expect(service.signup("[email protected]", "asdf")).rejects.toThrow(
      BadRequestException
    );
  });

change BadRequestException according to your listening exception

Tabitha answered 12/3, 2022 at 12:9 Comment(1)
Thanks very helpful, using expect().rejects.toThrow() was the only solution that worked for me.Melodramatize
L
4

Before v27, jest use jest-jasmine2 by default.

For version 27, jest uses jest-circus which doesn’t support done callback.

So you need to change the default testRunner.

Override with react-app-rewired worked for me

// config-overrides.js
module.exports.jest = (config) => {
    config.testRunner = 'jest-jasmine2';
    return config;
};
Livre answered 15/4, 2022 at 9:57 Comment(1)
Thank you, Your recomendation was usefull for meIon
W
2

Just use return instead of calling done():

it('throws an error if a user signs up with an email that is in use', async () => {
  fakeUsersService.find = () =>
    Promise.resolve([{ id: 1, email: 'a', password: '1' } as User]);
  try {
    await service.signup('[email protected]', 'asdf');
  } catch {
    return;
  }
});
Whitleywhitlock answered 10/4, 2023 at 17:57 Comment(0)
F
1

For the last version from jest, you can't use `async/await , promise and done together (Test functions cannot both take a 'done' callback and return something. Either use a 'done' callback, or return a promise.).

the solution is

user.entity.ts

import {
  Entity,
  Column,
  PrimaryGeneratedColumn,
  AfterInsert,
  AfterRemove,
  AfterUpdate,
} from 'typeorm';

@Entity()
export class User {
  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  email: string;

  @Column()

  password: string;

  @AfterInsert()
  logInsert() {
    console.log('Inserted User with id', this.id);
  }

  @AfterUpdate()
  logUpdate() {
    console.log('Updated User with id', this.id);
  }

  @AfterRemove()
  logRemove() {
    console.log('Removed User with id', this.id);
  }
}

auth.service.spec.ts

  it('throws an error if user signs up with email that is in use', async () => {
    fakeUsersService.find = () =>
      Promise.resolve([{ id: 1, email: '[email protected]', password: '1' } as User]);

    expect(async () => {
      const email = '[email protected]';
      const password = 'asdf';
      await service.signup(email, password);
    }).rejects.toThrow(BadRequestException);
  });
Facetiae answered 5/11, 2022 at 22:23 Comment(0)
R
0

Also, if you want to use both you can downgrade your current version of jest to : 26.6.3. Worked fine for me, I'm using async + done

Racehorse answered 11/3, 2022 at 14:53 Comment(2)
Please don't post answers like this because this is a comment. If you keep doing this you will be rendered unable to ask questions in SO due to the downvotes you going to receive. So please read this, I'm trying to help not being rood.Foster
This is an answer that worked for me.Supinator
N
0
it('throws an error if a user signs up with an email that is in use', async () => {
    await service.signup('[email protected]', 'asdf');
    try {
     await service.signup('[email protected]', 'asdf');
    } catch (e) {
      expect(e.toString()).toMatch('email in use');
    }
  });
Neither answered 7/11, 2022 at 8:49 Comment(1)
Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.Kingmaker
M
0

in order for it to work, you can do the following:

it('throws an error if a user signs up with an email that is in use', async () => {
fakeUsersService.find = () =>
  Promise.resolve([
    { id: 1, email: '[email protected]', password: 'somePassword' } as User,
  ]);
  expect(async () => {
  await service.signup('[email protected]', 'somePassword')
  }).rejects.toThrow(BadRequestException)
});
Murmurous answered 17/12, 2022 at 14:43 Comment(0)
A
-1

You can use this hack for some cases =)

it('should make an api request', (done) => {
  const asyncCall = async () => {
    await callbackWithApiInside();

    setTimeout(() => {
      expect(api).toHaveBeenNthCalledWith(1, payload);
      done();
    }, 1000);
  };

  asyncCall();
});
Archaic answered 9/1, 2023 at 11:47 Comment(1)
this is terrible and not what anyone should do. Flaky tests that now take a ton of time because of some setTimeout.Houchens

© 2022 - 2024 — McMap. All rights reserved.