Home > Blockchain >  Jest test fails when using a pipe, but succeeds when using a subscription
Jest test fails when using a pipe, but succeeds when using a subscription

Time:12-04

In the application, there is a service, which is used in an action that then updates the state.

When running the application everything works fine using a piped observable. However, when creating the test, I am not able to update the state without adding a subscription for it to work.

./MyApiService.ts

@Injectable({ providedIn: 'root' })
export class MyApiService {

  constructor(private http: HttpClient) {}

  getSomeData(): Observable<any> {
    return this.http.get('https://some-url.com/')
  }
}

./MyState.ts

@State<StateModel>({})
@Injectable()
export class MyState {
  constructor() {
    private myApiService: MyApiService,
  }

  @Action(SomeAction.UpdateData)
  updateData(ctx: StateContext<StateModel>) {
    return this.myApiService.getSomeData().pipe(
      tap(data => {
        if (data) {
          ctx.patchState({ data });
        }
      }
    )
  }
}

Now for the test:

./MyState.spec.ts

desribe('some test', () => {

  beforeEach(() => {
    stub.MyApiService = {
      getSomeData: jest.fn().mockReturnValue(of({ foo: 'bar' }]);
    }

    stub.ctx = {
      patchState: jest.fn(),
    }

    state = new MyState(stub.MyApiService);
  });

  it('should update data', () => {
    state.updateData(stub.ctx);
    expect(stub.MyApiService.getSomeData).toHaveBeenCalledTimes(1); // works
    expect(stub.ctx.patchState).toHaveBeenCalledTimes(1); // fails!
  });
});

That does not work.

However, if I change the action to have a subscription...

./MyState.ts

@State<StateModel>()
@Injectable()
export class MyState {
  constructor() {
    private myApiService: MyApiService,
  }

  @Action(SomeAction.UpdateData)
  updateData(ctx: StateContext<StateModel>) {
    return this.myApiService.getSomeData().pipe(
      tap(data => {
        if (data) {
          ctx.patchState({ data });
        }
      }
    ).subscribe(); // added here and now the test succeeds
  }
}

...it works. But it feels a bit off to have to subscribe and also have to unsubscribe when that is not necessary with just the pipe.

CodePudding user response:

what Matthieu explained is correct. If you dont subscribe to an async-call nothing will ever happend. the pipe() operator is only there to cut into the stream and change the data before it reaches the component/services. But without the Subscription the pipe() will never trigger.

Which means in your Tests, that you have to subscribe to get the data, just like in a normal component.

  • Related