1
votes

I have an Angular 5 app that uses NgRx Effects. One of the Effects handles Signup to a website by passing data entered by the user to a NodeJS Backend. The Effect works exactly as intended but when I try to unit test it with Marbles, I get a weird error.

This is the Effect:

@Effect()
  authSignup$ = this.actions$
    .ofType(AuthActions.TRY_SIGNUP)
    .switchMap((signUpData: {
      first_name: string,
      last_name: string,
      username: string,
      password: string,
      type: string,
      agreement: boolean
    }) =>
      this.httpClient.post(
      '/custom-endpoint',
      {
        first_name: signUpData.first_name,
        last_name: signUpData.last_name,
        username: signUpData.username,
        password: signUpData.password,
        type: signUpData.type,
        agreement: signUpData.agreement
      },
      {
        headers: new HttpHeaders({
          'Content-Type':  'application/json'
        })
      })
      .map((res) => {
        return new AuthActions.SuccessfulSignup();
      })
      .catch((error: {reason: string}) => {
        return Observable.of(new AuthActions.UnsuccessfulSignup({reason: error.reason}))
      })
    );

This is the Unit Test with Marbles. It follows Todd Motto's guidelines for testing NgRx Effects.

export class TestActions extends Actions {
  constructor() {
    super(empty());
  }

  set stream(source: Observable<any>) {
    this.source = source;
  }
}

export function getTestActions() {
  return new TestActions();
}

fdescribe('AuthEffects', () => {

  let actions$: TestActions;
  let effects: fromEffects.AuthEffects;

  beforeEach(() => {
    TestBed.configureTestingModule({
      imports: [
        CookieModule.forRoot(),
        StoreModule.forRoot({...fromGlobal.reducers}),
        RouterTestingModule,
        HttpClientTestingModule
      ],
      providers: [
        fromEffects.AuthEffects,
        CookieAesService,
        { provide: Actions, useFactory: getTestActions },
      ],
    });
    actions$ = TestBed.get(Actions);
    effects = TestBed.get(fromEffects.AuthEffects);
  });

  it('should attempt to Sign Up the user', () => {
    const trySignup = new fromActions.TrySignup({
      first_name: 'test',
      last_name: 'test',
      password: 'test',
      username: '[email protected]',
      type: 'test',
      agreement: false
    });
    const unsuccessfulSignup = new fromActions.UnsuccessfulSignup({ reason: 'test' });

    actions$.stream = hot('a', { a: trySignup });
    const expected = cold('b', { b: unsuccessfulSignup });
    expect(effects.authSignup$).toBeObservable(expected);
  });
});

Finally, this is the error I get in Karma:

Expected $.length = 0 to equal 1.
Expected $[0] = undefined to equal Object({ frame: 0, notification: Notification({ kind: 'N', value: UnsuccessfulSignup({ payload: Object({ reason: 'test' }), type: 'UNSUCCESSFUL_SIGNUP' }), error: undefined, hasValue: true }) }).

I deduce from this rather cryptic error message that the trySignup action was fired, but not the unsuccessfulSignup action. Does anyone know why this might be happening?

Thanks!

1
Hi Alex, Am also facing the same error. Were you able to resolve this?Bharat
@Bharat Hi, no I wasn't able to fix it. According to others who reported this bug in StackOverflow, there may be a bug with hot and/or cold Observables in the library, which causes this. I don't have more information at present.Alex Verzea

1 Answers

1
votes

Alex,

Your switchMap switches the stream over to the http.post stream, which you are not stubbing or returning. You need to emit an error from the HttpClientTestingModule first for the catch event to fire.

See the link below for an example:

https://medium.com/@Jestfer/testing-http-requests-in-angular-with-httpclienttestingmodule-3880ceac74cf