0

I am trying to convert a test that uses a promise to now instead use an observable...

previously I would do something like this in a beforeEach to give me access to the promise's resolve:

  ...
  let apiCall: (params: ApiParams) => Promise<any>;
  let apiCallResolve: (response: any) => void;

  beforeEach((done) => {
    const promise = new Promise((resolve: (response: any) => void, reject: (response: any) => void) => {
      apiCallResolve = resolve;
      done();
    });
    myService.setApiCall(() => { return promise; });
    ...

Then I could have my test do something like:

it('does a super cool thing', () => {
  apiCallResolve({ some: 'magical', data: '!!!' });
  expect(myService.toHaveDoneSomethingRadical).toEqual(true);
});

This works fine, but as soon as I change this to an observable like:

  beforeEach((done) => {
    const observable = new Observable(subscriber => {
      apiCallResolve = subscriber.next;
      done();
    });

When my test tries to resolve the data by calling apiCallResolve, nothing seems to happen.. The real world code is basically doing:

const observable = this.apiCall(params);
observable.subscribe((response: ApiResponse) => {
  console.log('hello!!!');  // <-- I never see this
});

Isn't next the thing that would be somewhat-equivalent to calling a promise's resolve function? Is there another way I can do this?

patrick
  • 9,290
  • 13
  • 61
  • 112
  • You should also make changes in your service, instead of promise, change it to an observable, did you do that already? Based on what you are saying, you only want to make those changes only in the spec file – Andres2142 Mar 16 '22 at 02:13
  • Does this answer your question? [Testing Observables with jest](https://stackoverflow.com/questions/50698405/testing-observables-with-jest) – Amer Mar 16 '22 at 07:44
  • @Andres2142 yes, that's what the last block of code is showing.. `const observable = this.apiCall(params);` ... ? – patrick Mar 16 '22 at 16:36
  • @patrick, what is the code for your function `apiCall()`? – Andres2142 Mar 16 '22 at 17:28
  • @Andres2142 in the real world it would be `return this.httpClient.get('/some/api');` but I am trying to mock this out in the test so it's simply returning that `observable` const that's defined in the `beforeEach` – patrick Mar 16 '22 at 21:32

1 Answers1

0

Probably there are other ways of testing an observable but, I would do it like this:

Demo Service:

apiCall(): Observable<any> {
 return this.httpClient.get('/some/api');
}

Spec file:

import { TestBed } from '@angular/core/testing';
import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';

import { DemoService } from './demo.service'

describe('DemoService', () => {
  let service: DemoService;
  let httpTestingController: HttpTestingController;

  beforeEach(() => {
    TestBed.configureTestingModule({
      providers: [DemoService],
      // Angular provides this module when using HttpClientModule
      imports: [HttpClientTestingModule] 
    });
    service = TestBed.inject(DemoService);
    httpTestingController = TestBed.inject(HttpTestingController);
  });

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

  it('should return value hello !!!', () => {
    service.apiCall().subscribe(response => {
      expect(response).toEqual('hello !!!')
    })
    const request = httpTestingController.expectOne('/some/api');
    expect(request.request.method).toEqual('GET');
    expect(request.flush('hello !!!'));
  })
});

Andres2142
  • 2,622
  • 2
  • 14
  • 19