34

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('asdf@asdf.com', 'asdf');
} catch (err) {
  done();
}
});
monkeyUser
  • 4,301
  • 7
  • 46
  • 95

9 Answers9

35

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();
});
Steven Scott
  • 10,234
  • 9
  • 69
  • 117
13

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("asdf@asdf.com", "asdf")).rejects.toThrow(
      BadRequestException
    );
  });

change BadRequestException according to your listening exception

Amr Abdalrahman Ahmed
  • 920
  • 1
  • 14
  • 19
  • Thanks very helpful, using expect().rejects.toThrow() was the only solution that worked for me. – Yariv May 29 '22 at 06:43
3

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;
};
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('asdf@asdf.com', 'asdf');
  } catch {
    return;
  }
});
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: 'typescript@nestjs.jestjs', password: '1' } as User]);

    expect(async () => {
      const email = 'asdf@asdf.com';
      const password = 'asdf';
      await service.signup(email, password);
    }).rejects.toThrow(BadRequestException);
  });
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

stark max
  • 63
  • 5
  • 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](https://stackoverflow.com/help/answering), I'm trying to help not being rood. – S4NDM4N Mar 12 '22 at 06:59
  • 1
    This is an answer that worked for me. – Ravi Luthra Aug 26 '22 at 16:47
0
it('throws an error if a user signs up with an email that is in use', async () => {
    await service.signup('asdf@asdf.com', 'asdf');
    try {
     await service.signup('asdf@asdf.com', 'asdf');
    } catch (e) {
      expect(e.toString()).toMatch('email in use');
    }
  });
Mehradi
  • 32
  • 7
  • 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](https://stackoverflow.com/help/how-to-answer). – Ethan Nov 11 '22 at 03:21
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: 'test@test.com', password: 'somePassword' } as User,
  ]);
  expect(async () => {
  await service.signup('test@test.com', 'somePassword')
  }).rejects.toThrow(BadRequestException)
});
starball
  • 20,030
  • 7
  • 43
  • 238
danny
  • 31
  • 5
-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();
});