NagRock / ts-mockito

Mocking library for TypeScript
MIT License
977 stars 93 forks source link

Verify() doesn't work with async methods ? #170

Closed mmarchois closed 5 years ago

mmarchois commented 5 years ago

Hi,

I need help about verify method. It doesn't seems to work with async methods.

Here is my test :

// ...

describe('CanRegisterSpecification', () => {
  const email = 'xx@xx.com';
  let userRepository: UserRepository;
  let canRegister: CanRegisterSpecification;

  beforeEach(() => {
    userRepository = mock(UserRepository);
    canRegister = new CanRegisterSpecification(instance(userRepository));
  });

  it('userCanRegister', async () => {
    verify(userRepository.findOneByEmail(email)).once(); // It doesn't work
    when(userRepository.findOneByEmail(email)).thenResolve(null); // It work's
    expect(await canRegister.isSatisfiedBy(email)).toBe(true);
  });
});

Here is the class i want to test :

// ...

export class CanRegisterSpecification implements ISpecification {
  constructor(
    private readonly userRepository: IUserRepository
  ) {}

  public isSatisfiedBy = async (email: string): Promise<boolean> => {
    return !((await this.userRepository.findOneByEmail(email)) instanceof User);
  };
}

Any idea ?

lordrip commented 5 years ago
  it('userCanRegister', async () => {

// At this point you haven't called   
// canRegister.isSatisfiedBy

verify(userRepository.findOneByEmail(email)).once(); // It doesn't work
    when(userRepository.findOneByEmail(email)).thenResolve(null); // It work's
    expect(await canRegister.isSatisfiedBy(email)).toBe(true);
  });

verify(userRepository.findOneByEmail(email)).once(); // It doesn't work
    when(userRepository.findOneByEmail(email)).thenResolve(null); // It work's
    expect(await canRegister.isSatisfiedBy(email)).toBe(true);
  });

I Which error are you facing? Looks to me that at the moment of the first verify() the method has not been called.

mmarchois commented 5 years ago

userRepository.findOneByEmail(email) is called inside await canRegister.isSatisfiedBy(email). I have the following error : Expected "findOneByEmail(strictEqual(mathieu@fairness.coop))" to be called 1 time(s). But has been called 0 time(s).

when(userRepository.findOneByEmail(email)).thenResolve(null);
verify(userRepository.findOneByEmail(email)).once();
lordrip commented 5 years ago
    it('userCanRegister', async () => {    
      // I mean, here we're checking if findOneByEmail was called, actually it was not called yet.
      verify(userRepository.findOneByEmail(email)).once(); // It doesn't work

      // Then we're mocking the return of findOneByEmail
      when(userRepository.findOneByEmail(email)).thenResolve(null); // It work's

      // after this line the method was called, not before :)
      expect(await canRegister.isSatisfiedBy(email)).toBe(true);
    });

I suggest to try this approach:

    it('userCanRegister', async () => {
      when(userRepository.findOneByEmail(email)).thenResolve(null); // It work's

      const expectedResult = await canRegister.isSatisfiedBy(email);
      expect(expectedResult).toBe(true);

      verify(userRepository.findOneByEmail(email)).once(); // It doesn't work
    });

basically first we're mocking the return of the findOneByEmail method and then we're actually calling isSatisfiedBy in order to be able to check its return and also making the call to findOneByEmail.

does this make sense to you? :)

lordrip commented 5 years ago

btw, sorry for the curiosity, but is this nestjs code? :)

mmarchois commented 5 years ago

It works :) !!! Thank you ! Yes, it is nestjs code. Like this famework. I'm going to enjoy making unit test with ts-mockito :)

mmarchois commented 5 years ago

One more question :

it('testPasswordNotMatch', () => {
    when(userRepository.findOneByEmail(email)).thenResolve(
      new User({email, password: 'hash'})
    );
    when(encryptionAdapter.compare('hash', 'plainPassword')).thenResolve(false);

    const handler = commandHandler.execute(command);
    expect(handler).rejects.toThrow(PasswordNotMatchException);

    verify(userRepository.findOneByEmail(email)).once(); // It works
    verify(encryptionAdapter.compare('hash', 'plainPassword')).once();  // It don't works
  });

The second verify doesn't work but if i add a try catch around the expect statement, it works. :thinking:

lordrip commented 5 years ago

I think that maybe you're using commandHandler inside the encriptionAdapter and since you're raising an exception, maybe this is bubbling up outside.

I'll suggest to try this.

expect(handler).rejects(new PasswordNotMatchException());

And check if there's a try... catch for handling such in the encriptionAdapter

mmarchois commented 5 years ago

Ok thank you. I can close this issue.