У меня довольно маленький Angular интерфейс, который получает большую часть своих данных с внешнего сервера.
Тесты дают мой gyp
Я могу протестировать простой компонент с помощью насмешливого сервиса, однако это общее решение "заменить вывод", а не реальный тест ... для этого я считаю, что мне нужно предоставить известный результат, когда служба вызывает внешний API.
Вот простой пример:
Определение объекта / интерфейса:
// alerts.ts
export interface Alert {
id: number;
display_message: string;
is_enabled: boolean;
}
Определение службы:
// alerts.service.ts
import { of as observableOf, Observable } from 'rxjs';
import { catchError } from 'rxjs/operators';
import { Alert } from './alerts';
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
export interface IAlertService {
getAlerts(): Observable<Alert[] | null>;
}
@Injectable()
export class AlertService implements IAlertService {
readonly baseUrl = '/api/alerts/';
alerts$: Observable<Alert[] | null>;
constructor(private _http: HttpClient) { }
getAlerts(): Observable<Alert[] | null> {
return this._http.get<Alert[] | null>(this.baseUrl).pipe(
catchError(error => {
console.log('in catch: ', error);
return observableOf(null);
}));
}
}
Код компонента:
// alerts/alerts.component.html
<div *ngIf="alerts" >
<div class="service-notice" *ngFor="let alert of alerts">
<p [innerHTML]="alert.display_message"></p>
</div>
</div>
и
// alerts/alerts.component.ts
import { Component, OnInit } from '@angular/core';
import { Alert } from '../alerts';
import { AlertService } from '../alerts.service';
@Component({
selector: 'naas-alerts',
templateUrl: './alerts.component.html',
styleUrls: ['./alerts.component.scss'],
})
export class AlertComponent implements OnInit {
alerts: Alert[] | null;
constructor(private alertService: AlertService) { }
ngOnInit() {
this.alertService.getAlerts().subscribe(data => {
this.alerts = data;
});
}
}
Затем я написал класс httpInterceptor, чтобы можно было определить строку, которую я хотел вернуть в тесте:
// testing_interceptors.ts
import {
HttpEvent, HttpInterceptor, HttpHandler, HttpRequest, HttpResponse, HTTP_INTERCEPTORS
} from '@angular/common/http';
import { Injectable, Injector } from '@angular/core';
import { of as observableOf, Observable } from 'rxjs';
@Injectable()
export class TestHttpInterceptor implements HttpInterceptor {
current_containers: string = '[]';
current_user: string = '';
alerts: string = '[]';
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
console.log('TestHttpInterceptor called');
const url_regex = /(\/api(?:\/[\w+\/]+)+)$/;
const url_path = url_regex.exec(request.url)[1];
if(request.method === "GET") {
if(url_path == '/api/alerts/') {
return observableOf(new HttpResponse({
status: 200,
body: this.alerts
}));
}
if(url_path == '/api/users/current/') {
return observableOf(new HttpResponse({
status: 200,
body: this.current_user
}));
}
if (url_path === '/api/users/current/containers/') {
return observableOf(new HttpResponse({
status: 200,
body: this.current_containers
}));
}
}
}
}
... и мой тест (да, у меня все еще есть некоторые старые _component_mock_ закомментированные):
// alerts/alerts.component.spec.test
import { HttpClientModule, HTTP_INTERCEPTORS, HttpClient } from '@angular/common/http';
import { TestBed, ComponentFixture } from '@angular/core/testing';
import { AlertComponent } from './alerts.component';
import { AlertService } from '../alerts.service';
import { AlertServiceMock } from '../alerts.service.mock';
import { TestHttpInterceptor } from '../testing_interceptors';
describe('AlertsComponent', () => {
let fixture: ComponentFixture<AlertComponent>;
let component: AlertComponent;
// let alertServiceMock: AlertServiceMock;
let testHttpInterceptor: TestHttpInterceptor;
beforeEach(() => {
// alertServiceMock = new AlertServiceMock();
testHttpInterceptor = new TestHttpInterceptor();
TestBed.configureTestingModule({
declarations: [ AlertComponent ],
providers: [
{provide: AlertService, useClass: AlertService },
{provide: HttpClient, useClass: HttpClientModule},
{provide: HTTP_INTERCEPTORS, useClass: TestHttpInterceptor, multi: true }
],
}).compileComponents();
fixture = TestBed.createComponent(AlertComponent);
component = fixture.componentInstance;
});
it('should be created', done => {
fixture.detectChanges();
expect(component).toBeTruthy();
done();
});
// it('should have no alerts with no data', () => {
// alertServiceMock.test_alert = null;
// fixture.detectChanges();
// const compiled = fixture.debugElement.queryAll(By.css('p'));
// expect(compiled.length).toBe(0);
// });
// it('should have one alert', () => {
// alertServiceMock.test_alert = [{
// id: 1,
// display_message: 'Foo',
// is_enabled: true,
// }];
// fixture.detectChanges();
// const compiled = fixture.debugElement.queryAll(By.css('p'));
// expect(compiled.length).toBe(1);
// });
});
Проблема в том, что когда я запускаю это, я получаю следующую ошибку:
TypeError: this.http.get is not a function
error properties: Object({ ngDebugContext: DebugContext_({ view: Object({ def: Object({ factory: Function, nodeFlags: 33669121, rootNodeFlags: 33554433, nodeMatchedQueries: 0, flags: 0, nodes: [ Object({ nodeIndex: 0, parent: null, renderParent: null, bindingIndex: 0, outputIndex: 0, checkIndex: 0, flags: 33554433, childFlags: 114688, directChildFlags: 114688, childMatchedQueries: 0, matchedQueries: Object({ }), matchedQueryIds: 0, references: Object({ }), ngContentIndex: null, childCount: 1, bindings: [ ], bindingFlags: 0, outputs: [ ], element: Object({ ns: '', name: 'naas-alerts', attrs: [ ], template: null, componentProvider: Object({ nodeIndex: 1, parent: <circular reference: Object>, renderParent: <circular reference: Object>, bindingIndex: 0, outputIndex: 0, checkIndex: 1, flags: 114688, childFlags: 0, directChildFlags: 0, childMatchedQueries: 0, matchedQueries: Object, matchedQueryIds: 0, references: Object, ngContentIndex: -1, childCount: 0, bindings: Array, bindingFlags: 0, outputs: Array ...
at <Jasmine>
at AlertService.getAlerts (http://localhost:9876/_karma_webpack_/src/app/alerts.service.ts:20:22)
at AlertComponent.ngOnInit (http://localhost:9876/_karma_webpack_/src/app/alerts/alerts.component.ts:18:23)
at checkAndUpdateDirectiveInline (http://localhost:9876/_karma_webpack_/node_modules/@angular/core/fesm2015/core.js:31910:1)
at checkAndUpdateNodeInline (http://localhost:9876/_karma_webpack_/node_modules/@angular/core/fesm2015/core.js:44367:1)
at checkAndUpdateNode (http://localhost:9876/_karma_webpack_/node_modules/@angular/core/fesm2015/core.js:44306:1)
at debugCheckAndUpdateNode (http://localhost:9876/_karma_webpack_/node_modules/@angular/core/fesm2015/core.js:45328:36)
at debugCheckDirectivesFn (http://localhost:9876/_karma_webpack_/node_modules/@angular/core/fesm2015/core.js:45271:1)
at Object.eval [as updateDirectives] (ng:///DynamicTestModule/AlertComponent_Host.ngfactory.js:10:5)
at Object.debugUpdateDirectives [as updateDirectives] (http://localhost:9876/_karma_webpack_/node_modules/@angular/core/fesm2015/core.js:45259:1)
at checkAndUpdateView (http://localhost:9876/_karma_webpack_/node_modules/@angular/core/fesm2015/core.js:44271:1)
Chrome 80.0.3987 (Linux 0.0.0): Executed 16 of 23 (1 FAILED) (0 secs / 2.133 secs)
Chrome 80.0.3987 (Linux 0.0.0) AlertsComponent should be created FAILED
TypeError: this.http.get is not a function
error properties: Object({ ngDebugContext: DebugContext_({ view: Object({ def: Object({ factory: Function, nodeFlags: 33669121, rootNodeFlags: 33554433, nodeMatchedQueries: 0, flags: 0, nodes: [ Object({ nodeIndex: 0, parent: null, renderParent: null, bindingIndex: 0, outputIndex: 0, checkIndex: 0, flags: 33554433, childFlags: 114688, directChildFlags: 114688, childMatchedQueries: 0, matchedQueries: Object({ }), matchedQueryIds: 0, references: Object({ }), ngContentIndex: null, childCount: 1, bindings: [ ], bindingFlags: 0, outputs: [ ], element: Object({ ns: '', name: 'naas-alerts', attrs: [ ], template: null, componentProvider: Object({ nodeIndex: 1, parent: <circular reference: Object>, renderParent: <circular reference: Object>, bindingIndex: 0, outputIndex: 0, checkIndex: 1, flags: 114688, childFlags: 0, directChildFlags: 0, childMatchedQueries: 0, matchedQueries: Object, matchedQueryIds: 0, references: Object, ngContentIndex: -1, childCount: 0, bindings: Array, bindingFlags: 0, outputs: Array ...
at <Jasmine>
at AlertService.getAlerts (http://localhost:9876/_karma_webpack_/src/app/alerts.service.ts:20:22)
at AlertComponent.ngOnInit (http://localhost:9876/_karma_webpack_/src/app/alerts/alerts.component.ts:18:23)
at checkAndUpdateDirectiveInline (http://localhost:9876/_karma_webpack_/node_modules/@angular/core/fesm2015/core.js:31910:1)
at checkAndUpdateNodeInline (http://localhost:9876/_karma_webpack_/node_modules/@angular/core/fesm2015/core.js:44367:1)
at checkAndUpdateNode (http://localhost:9876/_karma_webpack_/node_modules/@angular/core/fesm2015/core.js:44306:1)
at debugCheckAndUpdateNode (http://localhost:9876/_karma_webpack_/node_modules/@angular/core/fesm2015/core.js:45328:36)
at debugCheckDirectivesFn (http://localhost:9876/_karma_webpack_/node_modules/@angular/core/fesm2015/core.js:45271:1)
at Object.eval [as updateDirectives] (ng:///DynamicTestModule/AlertComponent_Host.ngfactory.js:10:5)
at Object.debugUpdateDirectives [as updateDirectives] (http://localhost:9876/_karma_webpack_/node_modules/@angular/core/fesm2015/core.js:45259:1)
Chrome 80.0.3987 (Linux 0.0.0): Executed 23 of 23 (1 FAILED) (2.504 secs / 2.346 secs)
... Я был кругом T'Inte rnet, и нашел много написания перехватчиков ... но меньше на их тестирование.
Я потратил слишком долго на это и мог бы советоваться.
- Является ли это действительно действующим решением для тестирования ( Я посмотрел на
marbles
, но обнаружил, что это еще менее понятно) - Как я могу заставить его работать?