Alex Verzea
Alex Verzea

Reputation: 441

NgRx Marbles Testing of an Effect returns weird error

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!

Upvotes: 1

Views: 1917

Answers (1)

Tyler Padley
Tyler Padley

Reputation: 50

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

Upvotes: 1

Related Questions