2

I have an observable that ultimately triggers a call to Angular HttpClient. I need to mock the response from the HttpClient, but I'm at a loss how to do it within the context of the TestScheduler run method. Say I have the following test:

it('observable should trigger call to HttpClient', () => {

    scheduler.run(helpers => {
        const { expectObservable, cold } = helpers;
        const transferObservable = cold('-a-', { a: someInput });
        transferService.value$ = transferObservable;

        sut = new MyService(transferService, apiService);
        const expectedMarble = '-a-';
        const expectedValues = { a: expectedData };
        expectObservable(sut.data$).toBe(expectedMarble, expectedValues);
    });
});

Values emitted on transferObservable cascade to a switchMap call to a service which in turn sends a GET request via Angular HttpClient; the resulting value is emitted from sut.data$.

I've tried placing following lines at various places within and outside the scheduler.run method, but I always get the same error: Expected one matching request for criteria "Match URL: http://foo", found none.

const request = httpMock.expectOne(`http://foo`);
request.flush(expectedContract);

Roughly where and how would I mock a single call to http://foo and flush expectedData as the desired response?

EDIT Stripped-down beforeEach setup showing configuration of HttpTestingController:

beforeEach(() => {

    scheduler = new TestScheduler((actual, expected) => {
        expect(actual).toEqual(expected);
    });

    transferService = jasmine.createSpyObj<TransferService>(['value$']);

    TestBed.configureTestingModule({
        imports: [HttpClientTestingModule],
        declarations: [],
        providers: [],
        schemas: [NO_ERRORS_SCHEMA]
    });

    httpMock = TestBed.inject(HttpTestingController);
});

EDIT 2: Screenshot of HttpClient instance from within my service when debugging.

enter image description here

EDIT 3: Stackblitz for issue here. The failing test contract$ should return contract is the one of interest.

tek
  • 343
  • 1
  • 4
  • 10
  • Are you using `HttpTestingController`? Can you show your setup for it? – wlf Feb 14 '22 at 22:16
  • @wlf I am; see edits above. – tek Feb 14 '22 at 22:26
  • The `expectOne` and `flush` lines should be OK just before `const expectedMarble = '-a-';`. Might be something else going on, can you verify (console.log or debug) that the http call is being made in your service? Also try remove the `-` around your marble data ie: `-a-` => `a` – wlf Feb 15 '22 at 02:09
  • @wlf Removing the dashes has no effect. I have verified that a testing handler appears to be getting injected into my service (see edit above). When I comment out `expectOne` and `flush` I get log messages up to the point of the actual HTTP call; however when uncomment them I get no log messages at all. It is as if they are somehow preventing a subscription. – tek Feb 15 '22 at 17:15
  • You could try creating a stack blitz for external debugging - you can fork from this one which has karma running, just go the preview to see the results. – wlf Feb 15 '22 at 21:14
  • @wlf Yes, seems it's getting about time to do that. Give me some time - it may have to be an evening or weekend project. For now, I'm proceeding without unit tests. :0 – tek Feb 15 '22 at 22:59
  • Link to stack blitz you can modify: https://stackblitz.com/edit/angular-testing-with-jasmine-f3vgbg?file=app%2Fresize-test.component.spec.ts – wlf Feb 15 '22 at 23:15
  • Are you running the test in `fakeAsync`? – Will Alexander Feb 18 '22 at 16:22
  • @WillAlexander I am not; is that recommended for this use case? – tek Feb 21 '22 at 10:17
  • I believe it's necessary so that you can call `tick()` and "pretend" that time has passed and the subscription has emitted – Will Alexander Feb 21 '22 at 14:55
  • @wlf Added Stackblitz; see edits above. Also @WillAlexander, I tried adding `fakeAsync()` and `tick()` without an observable change in behavior (not reflected in the StackBlitz link above). – tek Feb 27 '22 at 08:08
  • I looked at your stackblitz for some time but didn't get too far. Main problem is if you look at the console you are getting a `TypeError: Cannot read properties of undefined (reading 'pipe')` error which is preventing further execution of your test (and the failure). For some reason the `MyService` constructor gets called twice, the first time a real instance of `TransferService` is injected, the second time your mock is injected. When it runs the test it seems to use the former, which causes the error as there is no `value$` defined, so no `pipe` on it. – wlf Feb 28 '22 at 09:05
  • @wlf I updated the Stackblitz to initialize `TransferService.value$`; however I don't think that is/was the issue because `value$` is mocked in the test envmt, and the test `url$ should return url` has always passed. In any case, the error is gone now and the `contracts$ should return contract` test still fails for the same reason. – tek Mar 01 '22 at 06:35

0 Answers0