How to mock AWS Amplify library in Angular?

允我心安 提交于 2021-01-07 04:12:33

问题


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 ?


回答1:


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));
       
   service.getProfile.subscribe((prof) => expect(prof).toEqual(userProfile));
      
 });



回答2:


I manage to get rid of the error.

You need to import Auth in the test file and the types that will be needed in the following solution.

import { Auth } from 'aws-amplify';
import { ClientMetaData, SignInOpts } from '@aws-amplify/auth/src/types/Auth'; 

Now, redefining the signIn method of the Auth will solve the problem:

describe('AuthEffects', () => {
//...
Auth.signIn = (
    usernameOrSignInOpts: string | SignInOpts,
    pw?: string,
    clientMetadata: ClientMetaData = this._config.clientMetadata
  ) => of({}).toPromise();
//...

You need to follow the signature of the method to override it.

public signIn(
        usernameOrSignInOpts: string | SignInOpts,
        pw?: string,
        clientMetadata: ClientMetaData = this._config.clientMetadata
    ): Promise<CognitoUser | any> 

I don't know if it's the best (cleaner) solution to perform it, but I manage to get rid of the error and control the behavior of the Auth instance.




回答3:


Rather than import it directly into your service or Effects class, import it into your Module and inject it. Assuming you have an AuthService service class handling AWS Amplify's Auth.

In your corresponding module:

import Auth, { AuthClass } from '@aws-amplify/auth';
...
providers: [
    AuthService,
    { provide: AuthClass, useValue: Auth }
  ]

In your component or service, simply inject it like any other dependency:

constructor(
    private auth: AuthClass
    ) {
       auth.signIn('abc', 'def')
    }

And finally, in your spec file you mock it and use a Spy:

describe('AuthService', () => {
  let service: AuthService;
  let authSpy: jasmine.SpyObj<AuthClass>
  let authSpyObj = {
    signIn: () => new Promise<any>(() => true),
    signOut: () => new Promise<any>(() => true),
    signUp: () => new Promise<any>(() => true),
    confirmSignUp: () => new Promise<any>(() => true),
    resendSignUp: () => new Promise<any>(() => true),
  }

  beforeEach(() => {
    const spyAuth = jasmine.createSpyObj('auth', authSpyObj);

    TestBed.configureTestingModule({
      providers: [
        AuthService,
        { provide: AuthClass, useValue: spyAuth },
      ]
    })

    service = TestBed.inject(AuthService)
    authSpy = TestBed.inject(AuthClass) as jasmine.SpyObj<AuthClass>    
  });

  it('should be created', () => {
    expect(service).toBeTruthy();
  });

  it('should have called signIn', () => {
    service.signIn('a@b.c', '12345678')
    expect(authSpy.signIn).toHaveBeenCalled()
  })
});

This test file doesn't do much right now except confirm that your service/component is calling the expect Auth functions, but this should get you off the ground. Enjoy.



来源:https://stackoverflow.com/questions/59215244/how-to-mock-aws-amplify-library-in-angular

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!