我创建了一个可注入服务的类,我想测试返回Observable对象的函数。
一旦我尝试测试这样的函数,我就会收到以下错误:
TypeError: _this.handler.handle is not a function
我如何设法测试这种功能?
我在互联网上发现了很多例子,但大多数都采用了旧的Http模块,这个模块已被弃用。
这是我的注射类:
client.service.ts
import {Injectable} from '@angular/core';
import {HttpClient, HttpHeaders, HttpParams} from "@angular/common/http";
import {Observable} from "rxjs/Observable";
const BACKEND_PAGINATION_LIMIT = 25;
@Injectable()
/**
* Class who make requests on Alignak backend
* Injectable service
*/
export class BackendClient {
token: string;
url: string;
http: HttpClient;
/**
* @param {HttpClient} http - http client for requests
*/
constructor(http: HttpClient) {
this.http = http;
this.updateData()
}
/**
* Update data of backend: {@link url} and {@link token}
*/
private updateData(){
this.token = localStorage.getItem('token');
this.url = localStorage.getItem('url');
}
/**
* GET http function
* @param {string} endpoint - endpoint of request
* @param {HttpParams} params - http parameters of request
* @param {HttpHeaders} headers - htt headers of request
* @returns {Observable<Object>} - observable object
*/
private get(endpoint: string, params?: HttpParams, headers?: HttpHeaders): Observable<Object> {
this.updateData();
if (headers == null){
headers = new HttpHeaders()
.set('Accept', 'application/json')
.set('Authorization', this.token);
}
return this.http.get(
this.url + '/' + endpoint, {headers, params}
)
}
/**
* POST http function
* @param {string} endpoint - endpoint of request
* @param {Object} body - jsonable object to post
* @returns {Observable<Object>} - observable object
*/
private post(endpoint: string, body: Object): Observable<Object> {
return this.http.post(this.url + '/' + endpoint, body)
}
/**
* Post on "login" endpoint
* @param {string} username - username of backend
* @param {string} password - password of backend
* @returns {Observable<Object>} - observable object
*/
public login(username: string, password: string): Observable<any> {
let body = {
username: username,
password: password
};
return this.post('login', body)
}
}
并进行相应的测试:
client.service.spec.ts
import {async, TestBed} from '@angular/core/testing';
import {HttpClient, HttpHandler} from "@angular/common/http";
import {BackendClient} from "./client.service";
describe('BackendClient Service', () => {
let client: BackendClient;
beforeEach(async(() => {
TestBed.configureTestingModule({
providers: [BackendClient, HttpClient, HttpHandler],
});
}));
beforeEach(() => {
localStorage.setItem('url', '');
localStorage.setItem('token', '');
client = TestBed.get(BackendClient);
});
// This test works
it('Init BackendClient', () => {
expect(client.token).toEqual('');
expect(client.url).toEqual('');
expect(client.http instanceof HttpClient).toBe(true);
});
// This test fails and expect is not take in account
it('Login to Backend', () => {
client.url = 'http://demo.alignak.net:5000';
client.login('admin', 'admin')
.subscribe(
function (data) {
console.log('Received data: ', data);
expect(data['token'] != undefined).toBe(true)
},
err => console.log('Request ERR: ', err)
)
})
});
这是完整的输出错误:
....LOG: 'Request ERR: ', TypeError: _this.handler.handle is not a function
TypeError: _this.handler.handle is not a function
at MergeMapSubscriber.project (http://localhost:9876/base/test-config/karma-test-shim.js?c3c7bb07d085cf3d6c123f55b228352de66aee6a:66308:219)
at MergeMapSubscriber._tryNext (http://localhost:9876/base/test-config/karma-test-shim.js?c3c7bb07d085cf3d6c123f55b228352de66aee6a:104576:27)
at MergeMapSubscriber._next (http://localhost:9876/base/test-config/karma-test-shim.js?c3c7bb07d085cf3d6c123f55b228352de66aee6a:104566:18)
at MergeMapSubscriber.Subscriber.next (http://localhost:9876/base/test-config/karma-test-shim.js?c3c7bb07d085cf3d6c123f55b228352de66aee6a:36128:18)
at ScalarObservable._subscribe (http://localhost:9876/base/test-config/karma-test-shim.js?c3c7bb07d085cf3d6c123f55b228352de66aee6a:128360:24)
at ScalarObservable.Observable._trySubscribe (http://localhost:9876/base/test-config/karma-test-shim.js?c3c7bb07d085cf3d6c123f55b228352de66aee6a:23081:25)
at ScalarObservable.Observable.subscribe (http://localhost:9876/base/test-config/karma-test-shim.js?c3c7bb07d085cf3d6c123f55b228352de66aee6a:23069:93)
at MergeMapOperator.call (http://localhost:9876/base/test-config/karma-test-shim.js?c3c7bb07d085cf3d6c123f55b228352de66aee6a:104541:23)
at Observable.subscribe (http://localhost:9876/base/test-config/karma-test-shim.js?c3c7bb07d085cf3d6c123f55b228352de66aee6a:23066:22)
at FilterOperator.call (http://localhost:9876/base/test-config/karma-test-shim.js?c3c7bb07d085cf3d6c123f55b228352de66aee6a:137708:23)
at Observable.subscribe (http://localhost:9876/base/test-config/karma-test-shim.js?c3c7bb07d085cf3d6c123f55b228352de66aee6a:23066:22)
at MapOperator.call (http://localhost:9876/base/test-config/karma-test-shim.js?c3c7bb07d085cf3d6c123f55b228352de66aee6a:130125:23)
at Observable.subscribe (http://localhost:9876/base/test-config/karma-test-shim.js?c3c7bb07d085cf3d6c123f55b228352de66aee6a:23066:22)
at UserContext.<anonymous> (http://localhost:9876/base/test-config/karma-test-shim.js?c3c7bb07d085cf3d6c123f55b228352de66aee6a:138452:14)
at ZoneDelegate.invoke (http://localhost:9876/base/test-config/karma-test-shim.js?c3c7bb07d085cf3d6c123f55b228352de66aee6a:123739:26)
at ProxyZoneSpec.onInvoke (http://localhost:9876/base/test-config/karma-test-shim.js?c3c7bb07d085cf3d6c123f55b228352de66aee6a:126726:39)
at ZoneDelegate.invoke (http://localhost:9876/base/test-config/karma-test-shim.js?c3c7bb07d085cf3d6c123f55b228352de66aee6a:123738:32)
at Zone.run (http://localhost:9876/base/test-config/karma-test-shim.js?c3c7bb07d085cf3d6c123f55b228352de66aee6a:123489:43)
at runInTestZone (http://localhost:9876/base/test-config/karma-test-shim.js?c3c7bb07d085cf3d6c123f55b228352de66aee6a:126987:34)
at UserContext.<anonymous> (http://localhost:9876/base/test-config/karma-test-shim.js?c3c7bb07d085cf3d6c123f55b228352de66aee6a:127002:20)
at ZoneQueueRunner.attempt (http://localhost:9876/base/node_modules/jasmine-core/lib/jasmine-core/jasmine.js?daba65c98fa088349a3e9d7df843a63405ccfc15:4816:44)
at ZoneQueueRunner.QueueRunner.run (http://localhost:9876/base/node_modules/jasmine-core/lib/jasmine-core/jasmine.js?daba65c98fa088349a3e9d7df843a63405ccfc15:4854:25)
at runNext (http://localhost:9876/base/node_modules/jasmine-core/lib/jasmine-core/jasmine.js?daba65c98fa088349a3e9d7df843a63405ccfc15:4784:18)
at next (http://localhost:9876/base/node_modules/jasmine-core/lib/jasmine-core/jasmine.js?daba65c98fa088349a3e9d7df843a63405ccfc15:4791:11)
at http://localhost:9876/base/node_modules/jasmine-core/lib/jasmine-core/jasmine.js?daba65c98fa088349a3e9d7df843a63405ccfc15:4709:12
at http://localhost:9876/base/test-config/karma-test-shim.js?c3c7bb07d085cf3d6c123f55b228352de66aee6a:52171:17
at ZoneDelegate.invoke (http://localhost:9876/base/test-config/karma-test-shim.js?c3c7bb07d085cf3d6c123f55b228352de66aee6a:123739:26)
at AsyncTestZoneSpec.onInvoke (http://localhost:9876/base/test-config/karma-test-shim.js?c3c7bb07d085cf3d6c123f55b228352de66aee6a:127212:39)
at ProxyZoneSpec.onInvoke (http://localhost:9876/base/test-config/karma-test-shim.js?c3c7bb07d085cf3d6c123f55b228352de66aee6a:126723:39)
at ZoneDelegate.invoke (http://localhost:9876/base/test-config/karma-test-shim.js?c3c7bb07d085cf3d6c123f55b228352de66aee6a:123738:32)
at Zone.run (http://localhost:9876/base/test-config/karma-test-shim.js?c3c7bb07d085cf3d6c123f55b228352de66aee6a:123489:43)
at AsyncTestZoneSpec.finishCallback (http://localhost:9876/base/test-config/karma-test-shim.js?c3c7bb07d085cf3d6c123f55b228352de66aee6a:52166:25)
at http://localhost:9876/base/test-config/karma-test-shim.js?c3c7bb07d085cf3d6c123f55b228352de66aee6a:127155:31
at ZoneDelegate.invokeTask (http://localhost:9876/base/test-config/karma-test-shim.js?c3c7bb07d085cf3d6c123f55b228352de66aee6a:123772:31)
at Zone.runTask (http://localhost:9876/base/test-config/karma-test-shim.js?c3c7bb07d085cf3d6c123f55b228352de66aee6a:123539:47)
at ZoneTask.invokeTask (http://localhost:9876/base/test-config/karma-test-shim.js?c3c7bb07d085cf3d6c123f55b228352de66aee6a:123847:34)
at ZoneTask.invoke (http://localhost:9876/base/test-config/karma-test-shim.js?c3c7bb07d085cf3d6c123f55b228352de66aee6a:123836:48)
at timer (http://localhost:9876/base/test-config/karma-test-shim.js?c3c7bb07d085cf3d6c123f55b228352de66aee6a:125405:29)
看起来HttpClient客户端的处理程序未定义...并且不考虑订阅中的测试。
Mock解决方案:
import {TestBed, getTestBed} from '@angular/core/testing';
import {HttpClient} from "@angular/common/http";
import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';
import {BackendClient} from "./client.service";
describe('BackendClient Service', () => {
let injector: TestBed;
let service: BackendClient;
let httpMock: HttpTestingController;
beforeEach(() => {
TestBed.configureTestingModule({
imports: [HttpClientTestingModule],
providers: [BackendClient]
});
injector = getTestBed();
localStorage.setItem('token', 'my-long-token');
localStorage.setItem('url', 'http://demo.alignak.net:5000');
service = injector.get(BackendClient);
httpMock = injector.get(HttpTestingController);
});
afterEach(() => {
httpMock.verify();
});
it('Init BackendClient', () => {
expect(service.token).toEqual('my-long-token');
expect(service.url).toEqual('http://demo.alignak.net:5000');
expect(service.http instanceof HttpClient).toBe(true);
});
// This test fails and expect is not take in account
it('Login to Backend', () => {
const dummyToken = [
{ token: 'my-received-token' }
];
service.login('admin', 'admin').subscribe(
token => {
expect(token.length).toBe(1);
expect(token).toEqual(dummyToken);
});
const req = httpMock.expectOne(`${service.url}/login`);
expect(req.request.method).toBe("POST");
expect(req.request.url).toBe(`${service.url}/login`);
expect(req.request.body).toEqual({username: 'admin', password: 'admin'});
req.flush(dummyToken);
})
});
答案 0 :(得分:0)
首先:
http: HttpClient;
constructor(http: HttpClient) {
this.http = http;
}
这是重复的代码。将变量作为参数放入构造函数会创建该类的成员。在这里,您要创建两次。我很惊讶你的短信并没有告诉你你有一个阴影变量。
其次,当您想要测试进行HTTP调用的服务时,您应该嘲笑您的后端。在这里,您没有嘲笑它,您正在进行真正的HTTP调用。那不是单元测试。
如果您不知道如何模拟后端,快速谷歌搜索会为您提供结果like this one。
最后,这个错误来自于你有一个拦截你的请求的拦截器。嘲笑你的后端将摆脱所述拦截器,删除你的问题。