如何对此登录功能进行单元测试,特别是http post部分?我做的http模拟没有正确编码进入代码的'if ... else'部分。我不想使用TestBed。 TestBed太慢了。
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');
}
以下是Jasmine测试我尝试:我需要帮助这条线。
spyOn(mockHttp,'post').and.returnValue(Observable.of(response));
我的returnValue应该在登录函数中使用'if ... else'代码?
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);
});
});
答案 0 :(得分:1)
这是我最终使用的答案。当我可以像下面的代码一样使用TestBed时,我以一种缓慢的方式使用TestBed:
import { TestBed, inject} from '@angular/core/testing';
import { HttpModule, XHRBackend, Response, ResponseOptions } from '@angular/http';
import { AuthenticationService } from '../_services/authentication.service';
import { MockBackend } from '@angular/http/testing';
describe('AuthenticationService', () => {
let mockbackend, service;
beforeEach(() => {
localStorage.clear();
TestBed.configureTestingModule({
imports: [ HttpModule ],
providers: [
AuthenticationService,
{ provide: XHRBackend, useClass: MockBackend }
]
});
});
beforeEach(inject([AuthenticationService, XHRBackend], (_service,
_mockbackend) => {
service = _service;
mockbackend = _mockbackend;
}));
it('should set access token in local storage for successful login', () =>
{
const access_token = 'blah83balc380';
const username = 'test';
const currentUserExpected = JSON.stringify({ username: username, token: access_token });
const response = {access_token: access_token};
const responseOptions = new ResponseOptions();
responseOptions.body = JSON.stringify(response);
mockbackend.connections.subscribe(connection => {
connection.mockRespond(new Response(responseOptions));
});
service.login(username, 'test').subscribe(respond => {
expect(respond).toEqual(true);
const currentUser = localStorage.getItem('currentUser');
expect(currentUserExpected).toEqual(currentUser);
});
});
it('should not set access token in local storage for unsuccessful login', () => {
const username = 'test';
const responseOptions = new ResponseOptions();
responseOptions.body = '';
responseOptions.status = 401;
const response = new Response(responseOptions);
response.ok = false;
response.statusText = 'Unauthorized';
response.type = 2;
mockbackend.connections.subscribe(connection => {
connection.mockRespond(response);
});
service.login(username, 'test').subscribe(respond => {
expect(respond).toEqual(false);
}, err => {
const currentUser = localStorage.getItem('currentUser');
expect(currentUser).toEqual(null);
});
});
});
答案 1 :(得分:0)
答案 2 :(得分:0)
NonEmpty
通常是测试Angular服务的首选方式。
尽管官方指南说,
独立单元测试本身检查一个类的实例,而不依赖于Angular或任何注入的值。测试人员使用new创建类的测试实例,根据需要为构造函数参数提供测试双精度,然后探测测试实例API表面。
您应该为管道和服务编写独立的单元测试。
孤立的测试不能解决DI测试问题。使用TestBed
实例化类时,其DI装饰器(new
,@Injectable
)未经过测试。
@Inject
测试在涉及Http
时也更容易编写和维护。
当性能成为真正的问题时,某些测试可以从MockBackend
转换为隔离。在这种情况下,应使用Jasmine模拟复制TestBed
API。为了获得全面覆盖,应测试所有函数调用。测试看起来像
Http
然后使用没有 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);
}));
的{{1}}执行另一项测试。
应该注意的是,bodyMock
也应该被删除,以便进行适当的测试。出于可测试性原因,通过DI使用本地存储服务是有益的。