0

I'm quite new to Unit testing so bear with me

I'm trying to unit test a service that is used to log the user with Amplify in an Angular application.

Right now in the spec file I'm doing:

 beforeEach(async () => {
        TestBed.configureTestingModule({
            imports: [
                HttpClientTestingModule,
            ],
            providers: [
                MyService, Amplify
            ]
        }
      myService = TestBed.get(MyService)
      amplify = TestBed.get(Amplify)
    })

   it('should login', async () => {
      const objToBeReturned = { signInUserSession: { idToken: { jwtToken: 'tokenValue' } } }
      spyOn(Amplify.Auth, 'signIn').and.returnValue(objToBeReturned)

      await myService.login('username', 'password')
   })

While in MyService :

  public async login(username: string, password: string) {
    const authUser = await Amplify.Auth.signIn(username.toLowerCase(), password)
    if (authUser.signInUserSession != null) {
      const idToken = authUser.signInUserSession.idToken.jwtToken
      return this.patientLogin(idToken)
    }
   }

  private async patientLogin(idToken?: string): Promise<boolean> {
    await this.sendRequest<LoginResponse>(url, data).pipe(
      tap(response => {
        if (!isLoginResponse(response)) {
          throw throwErr({ code: 'Generic' })
        }
        this.token = response.token
      })
    ).toPromise()
    return true
  }

This gives me the error Async function did not complete within 5000ms

I'm pretty sure it depends on the way I'm mocking Amplify

How do i properly mock it?

Tommazo
  • 87
  • 1
  • 11
  • Your service doesn't seem to use the injected Amplify, it calls it directly. – jonrsharpe Feb 28 '20 at 14:50
  • any suggestion on how to do it properly? the problem is that if I don't mock the _signin_ method I get the error: **Cannot read property 'clientMetadata' of undefined** – Tommazo Feb 28 '20 at 15:01

2 Answers2

1

Try:

   it('should login', async (done) => {
      spyOn(myService, 'patientLogin'); // assuming it is a public function
      const objToBeReturned = { signInUserSession: { idToken: { jwtToken: 'tokenValue' } } }
      // Promise.resolve is optional but since it is returning a promise and we are awaiting it, I think we should do it here as well.
      spyOn(Amplify.Auth, 'signIn').and.returnValue(Promise.resolve(objToBeReturned));
      console.log('calling login');
      await myService.login('username', 'password');
      // fixture.whenStable() can be optional as well, but I think it will be good to wait for all promises to finish
      console.log('login resolved');
      await fixture.whenStable();
      expect(myService.patientLogin).toHaveBeenCalledWith('tokenValue');
      // call the done function to tell the test you are done, I think this is what you were missing
      done();
   })

====== Edit ===============

You have to find out where the test is getting stuck, I am thinking this.patientLogin(idToken) is asynchronous and is getting stuck there. Look at the console.logs, make sure you see login resolved. Based on this hunch, I have spied on patientLogin, and just assert it was called.

AliF50
  • 16,947
  • 1
  • 21
  • 37
  • Thanks but unfortunately it does't work. keep getting the same error – Tommazo Feb 28 '20 at 15:10
  • You were right. in the sense that the test gets stuck on _patientLogin_ (which I just added to my question above) So if i add `spyOn(communication, 'patientLogin')` it does work. I'm a bit skeptical though, doesn't spying on the service you are testing defeat the purpose of the testing? – Tommazo Feb 28 '20 at 15:40
  • 1
    Well since you are testing just the login, then this is `it('should login')` is a good test. Then you can create another test of `it('patient login should work')` and test `patientLogin`. That's why it is called unit tests, you can tests bits/units of your code and have confidence that if every bit is doing its job then they should do their jobs as a whole. – AliF50 Feb 28 '20 at 15:46
0

You can use jasmine method callThrough and then you can check whether the method of the Auth class has been called by your service:

it('should login', async () => {
   spyOn(Auth, 'signIn').and.callThrough();

   await myService.login('username', 'password');
   expect(Auth['signIn']).toHaveBeenCalled();
});
Santi Barbat
  • 2,097
  • 2
  • 21
  • 26