How do I unit test this login function, specifically the http post part? The http mock I made is not coded correctly to get into the \'if...else\' section of the code. I d
TestBed
is generally preferable way to test Angular services.
Despite what the official guide says,
Isolated unit tests examine an instance of a class all by itself without any dependence on Angular or any injected values. The tester creates a test instance of the class with new, supplying test doubles for the constructor parameters as needed, and then probes the test instance API surface.
You should write isolated unit tests for pipes and services.
isolated tests don't address DI testing. When a class is instantiated with new
, its DI decorators (@Injectable
, @Inject
) are not tested.
Http
tests are also easier to write and maintain when MockBackend
is involved.
When performance becomes a real concern, some tests can be converted from TestBed
to isolated. In this case Http
API should be replicated with Jasmine mocks. In order to get full coverage, all functions calls should be tested. The test will look like
mockHttp = jasmine.createSpyObj(['post']);
service = new AuthenticationService(mockHttp);
...
it(..., fakeAsync(async () => {
const bodyMock = { access_token: 'foo' };
const responseMock = { json: jasmine.createSpy().and.returnValue(bodyMock) };
const responseMock$ = Observable.of(responseMock);
mockHttp.post.and.returnValue(responseMock$);
const login$ = service.login(...);
expect(mockHttp.post).toHaveBeenCalledTimes(1);
const postArgs = callback.calls.first().args;
expect(postArgs).toEqual([..., ..., jasmine.any(RequestOptions));
const requestOptions = postArgs[2];
expect(requestOptions.headers).toEqual(jasmine.any(Headers));
expect(Array.from(requestOptions.headers._headers)).toEqual([
['Content-Type', ['application/json']],
['accept', ['application/json']]
]);
expect(login$).toEqual(jasmine.any(Observable));
const login = await login$.toPromise();
expect(responseMock.json).toHaveBeenCalled();
expect(service.token).toBe('foo');
expect(localStorage.setItem).toHaveBeenCalledWith(...);
expect(login).toBe(true);
}));
Then another test is performed with bodyMock
that doesn't have access_token
.
It should be noticed that localStorage
should be stubbed as well in order to be properly tested. For testability reasons it's beneficial to use local storage service via DI instead.