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.