Search code examples
angulartypescriptunit-testingmockingaws-amplify

How to mock AWS Amplify library in Angular?


I am getting an error that seems to come from AWS Amplify when I run the suite of tests with Karma.

AuthEffects
    login
      √ should not dispatch any action
      √ should call setItem on LocalStorageService
Chrome 78.0.3904 (Windows 10.0.0) ERROR
  An error was thrown in afterAll
  Uncaught TypeError: Cannot read property 'clientMetadata' of undefined thrown

From that I suppose that this error is thrown from the last test that was launched: AuthEffects

In my AuthEffects, I had to do that to make AWS amplify working

import { Auth } from 'aws-amplify';
//...

const promise = Auth.signIn(username, password);

I don't understand how I can mock this API access to the Cognito. Usually, I provide a Mock service to the constructor by dependendies injection to avoid real connection to the API. Here it's directly imported in the component.

Spec file:

describe('login', () => {
    it('should not dispatch any action', () => {
      const actions = new Actions(EMPTY);
      const effect = new AuthEffects(
      //...
      );
      const metadata = getEffectsMetadata(effect);

      expect(metadata.login).toEqual({ dispatch: false });
    });

    it('should call setItem on LocalStorageService', () => {
      const loginAction = new ActionAuthLogin('test', 'Test1234!');
      const source = cold('a', { a: loginAction });
      const actions = new Actions(source);
      const effect = new AuthEffects(
      //...
      );

      effect.login.subscribe(() => {
        expect(localStorageService.setItem).toHaveBeenCalledWith(AUTH_KEY, {
          isAuthenticated: true
        });
      });
    });

    afterAll(() => {
      TestBed.resetTestingModule();
    });
  });

Is there a way to override this import from the spec file ?


Solution

  • You can make it like this:

    // service with Auth
    import { Injectable } from '@angular/core';
    import Auth, { CognitoUser } from '@aws-amplify/auth';
    ...
    
    private getProfile(): void {
       return from(Auth.currentUserInfo());
    }
    
    
    // service.spec.ts
    it('should set user and user profile', async () => {
       const userProfile = { profile: 'userProfile' } as any;
        
       Auth.currentUserInfo = jasmine.createSpy().and.callFake(() => Promise.resolve(userProfile));
           
       const resp = await service.getProfile.toPromise();
       expect(resp).toEqual(userProfile)
          
     });