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.