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 dont want to use TestBed. TestBed is too slow.
login(username: string, password: string): Observable<boolean> {
const headers = new Headers();
headers.append('Content-Type', 'application/json');
headers.append('accept', 'application/json');
let options = new RequestOptions({ headers: headers });
return this.http.post('https://blah/api/login',
JSON.stringify({ username: username, password: password }), options)
.map((response: Response) => {
const token = response.json() && response.json().access_token;
if (token) {
this.token = token;
localStorage.setItem('currentUser', JSON.stringify({ username: username, token: token }));
return true;
} else {
return false;
}
}).catch(this._serverError);
}
private _serverError(err: any) {
return Observable.throw(err || 'backend server error');
}
Below is the Jasmine test Im attempting: I need help with this line.
spyOn(mockHttp,'post').and.returnValue(Observable.of(response));
What should my returnValue be to get my inside of the 'if...else' code in the login function?
describe('AuthenticationService', () => {
let service: AuthenticationService;
let mockHttp = null;
beforeEach(() => {
mockHttp = {};
mockHttp.post = function(){};
service = new AuthenticationService(mockHttp);
});
it(`should set access token in local storage for successful login`,() => {
const access_token = 'blah83balc380';
const responseOptions = new ResponseOptions();
responseOptions.status = 200;
responseOptions.body = {access_token:access_token};
const username = 'test';
const currentUserExpected = JSON.stringify({ username: username, token: access_token });
var response = new Response(responseOptions);
spyOn(mockHttp,'post').and.returnValue(Observable.of(response));
service.login(username, 'test');
var currentUser = localStorage.getItem('currentUser');
expect(currentUserExpected).toEqual(currentUser);
});
});
TestBed
is generally preferable way to test Angular services.Despite what the official guide says,
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 whenMockBackend
is involved.When performance becomes a real concern, some tests can be converted from
TestBed
to isolated. In this caseHttp
API should be replicated with Jasmine mocks. In order to get full coverage, all functions calls should be tested. The test will look likeThen another test is performed with
bodyMock
that doesn't haveaccess_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.This is the answer that I ended up using. I was using TestBed in a way that was slow when I could have been using TestBed like the code below:
You can inject services into your test like
...
this will cause your post call to return with that mock data. What I am not certain of though is if you can actually inject XHRBackend without first doing a testbed and providing