Unit testing value of Observable returned from service (using async pipe)

Angular provides the utilities for testing asynchronous values. You can use the async utility with the fixture.whenStable method or the fakeAsync utility with the tick() function. Then using the DebugElement, you can actually query your template to be sure the values are getting loaded correctly.

Both methods for testing will work.

Using the async utility with whenStable:

Keep your setup the same, you're good to go there. You'll need to add some code to get ahold of the list's debug element. In your beforeEach:

const list = fixture.debugElement.query(By.css('list-group'));

Then you can dig in to that list and get individual items. I won't go too far into how to use DebugElement because that's outside the scope of this question. Learn more here: https://angular.io/guide/testing#componentfixture-debugelement-and-querybycss.

Then in your unit test:

 it('should get the dashboard items when initialized', async(() => {
        fixture.detectChanges();
        
        fixture.whenStable().then(() => { // wait for your async data
          fixture.detectChanges(); // refresh your fake template
          /* 
             now here you can check the debug element for your list 
             and see that the items in that list correctly represent 
             your mock data 
             e.g. expect(listItem1Header.textContent).toEqual('list item 1');
           */
        }
    }));

Using the fakeAsync utility with tick:

it('should get the dashboard items when initialized', fakeAsync(() => {
        fixture.detectChanges();
        tick(); // wait for async data
        fixture.detectChanges(); // refresh fake template
        /* 
           now here you can check the debug element for your list 
           and see that the items in that list correctly represent 
          your mock data 
           e.g. expect(listItem1Header.textContent).toEqual('list item 1');
        */
}));

So in summary, do not remove the async pipe from your template just to make testing easier. The async pipe is a great utility and does a lot of clean up for you, plus the Angular team has provided some very useful testing utilites for this exact use-case. Hopefully one of the techniques above works. It sounds like using DebugElement and one of the aforementioned utilities will help you lots :)


There is a package for testing observables jasmine-marbles
With it you can write test like this:

...
spy = spyOn(viperDashboardService, 'getDashboardItems')
            .and.returnValue(cold('-r|', { r: mockItems }));
...
it('should show the dashboard after component initialized', () => {
        const items$ = cold('-r|', { r: mockItems });
        expect(component.items).toBeObservable(items$);
    });

But probably it is not the best example - I normally do use this package to test chains of observables. For example if I have service that do inside some .map()ing with input observable - I can mock original observable and then create a new one and compare with service results.

Also neither async nor fakeAsync will work with time dependent observable operations, but with jasmine-marbles you can inject test scheduler into them and it will work like a charm and without any timeouts - instantly! Here I have an example how to inject a test scheduler.