Angular 2使用可观测的组件测试组件而不嘲笑
我一直在尝试使用Jasmine为Angular 2项目创建集成测试。我可以嘲笑服务和数据,并运行适当的单元测试,但现在我想测试以确保我的组件可以使用该服务实际连接到后端API。到目前为止,我可以确认我的组件和服务中的方法是由我的间谍引发的,但是在组件内的.subscribe(....)方法直到测试完成/失败之后才会完成。我对我的规格代码如下:Angular 2使用可观测的组件测试组件而不嘲笑
import { async, ComponentFixture, TestBed, inject, fakeAsync } from '@angular/core/testing';
import { DebugElement } from "@angular/core";
import { By } from "@angular/platform-browser";
import { ReviewComponent } from './review.component';
import { FormsModule } from '@angular/forms';
import { UrlService } from 'app/shared/services/url.service';
import { HttpModule, Http } from '@angular/http';
import { ReviewService } from "./review.service";
describe('CommunicationLogComponent',() => {
let component: ReviewComponent;
let fixture: ComponentFixture<ReviewComponent>;
let serviceSpy: jasmine.Spy;
let componentSpy: jasmine.Spy;
let service: ReviewService;
beforeEach(() => {
TestBed.configureTestingModule({
imports: [FormsModule, HttpModule],
providers: [UrlService],
declarations: [ReviewComponent]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(ReviewComponent);
component = fixture.componentInstance;
component.processId = 6;
service = fixture.debugElement.injector.get(ReviewService);
componentSpy = spyOn(component, "ngOnInit").and.callThrough();
serviceSpy = spyOn(service, 'getReviewByProcess').and.callThrough();
});
it('should create component',() => {
expect(component).toBeTruthy();
});
it('should not have called ngOnInit() yet',() => {
expect(componentSpy).not.toHaveBeenCalled();
});
it('should make a call to ngOnInit() and by extension getReviewByProcess',() => {
fixture.detectChanges();
expect(componentSpy).toHaveBeenCalled();
expect(serviceSpy).toHaveBeenCalled();
});
//ISSUES HERE
it('should show review after the getReviewByProcess resolves', async(() => {
fixture.detectChanges();
fixture.whenStable().then(() => {
expect(componentSpy).toHaveBeenCalled();
fixture.detectChanges();
expect(component.Reviews[0]).not.toBe(undefined);
});
}), 5000);
});
所有的测试,直到一个标有“//问题对于这个问题”工作正常,并通过。
我正在使用async()并在试图完成我的测试之前等待灯具变得稳定。它还通过componentSpy确认ngOnInit已被调用(它将连接到后端并订阅Review)。
我的组件的代码如下所示:
import { Component, EventEmitter, Input, OnInit } from '@angular/core';
//Service
import { ReviewService } from "app/process/monitoring/shared/components/review/review.service";
//Classes
import { Review } from "app/process/monitoring/shared/components/review/review";
@Component({
providers: [
ReviewService
],
selector: 'monitoring-review',
templateUrl: './review.component.html',
styleUrls: ['./review.component.css']
})
export class ReviewComponent implements OnInit {
public Review: Review = new Review();
public Reviews: Review[] = [];
@Input() public processId: number;
constructor(
private ReviewService: ReviewService
) { }
ngOnInit(): void {
this.ReviewService.getReviewByProcess(this.processId, true).first().subscribe(
Reviews => {
if (Reviews) {
this.Reviews = Reviews //doesn't trigger until test is finished
}
},
error => console.log(error)
);
}
}
最后我ReviewService看起来是这样的:
import { Injectable } from '@angular/core';
import { Http, Response } from '@angular/http';
import { Headers, RequestOptions, RequestMethod, URLSearchParams } from '@angular/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/observable/throw';
import 'rxjs/add/operator/catch';
import 'rxjs/add/operator/map';
// App-wide Services
import { UrlService } from 'app/shared/services/url.service';
// Classes
import { Review } from "app/process/monitoring/shared/components/review/review";
@Injectable()
export class ReviewService {
private devUrl: string;
private jsonHeaders: Headers = new Headers({ 'Content-Type': 'application/json' });
private prodUrl: string;
private reviewPath: string = 'monitoring/program/';
constructor(
private urlService: UrlService,
private http: Http
) {
this.devUrl = this.urlService.getUrl('dev').url;
this.prodUrl = this.urlService.getUrl('prod').url;
}
getReviewByProcess(processId: number, isProd: boolean = false): Observable<Review[]> {
let targetUrl: string = (isProd ? this.prodUrl : this.devUrl) + this.reviewPath + "/" + processId;
return this.http.get(targetUrl, { withCredentials: true })
.map((res: Response) => res.json())
.catch((error: any) => Observable.throw(error.json().error || 'Server error')
);
};
}
使用噶调试器,我可以证实,我测试调用ngOnInit,然后调用该服务,但直到我的测试完成之后,它才会触及我的组件订阅的内部部分(标记为“//不触发”)。
总结:我想创建一个测试,可以测试我的组件与我的服务集成,使用“实时”后端来收集我的数据。这是服务,我的组件不会嘲笑任何东西。我可以确认我的方法已经达到,但在ReviewComponent的ngOnInit()方法中找到的.subscribe(...)不会完成,直到测试完成/失败。
你需要你的服务来返回模拟数据。类似的东西:
spyOn(service, 'getReviewByProcess')
.and.returnValue(Observable.of(new Array<Review>()));
您需要返回可观察和测试中订阅它的异步方法来等待它。例如,下面的假设测试会起作用。
it('', async(() => {
observable().subscribe(
(review) => {
expect(component.Reviews[0]).not.toBe(undefined)
}
)
}));
问题是您没有等待异步调用在测试中执行。您可以稍微改变角度http服务,例如:
import { Http, HttpModule, BaseRequestOptions, XHRBackend, HttpModule, ConnectionBackend } from '@angular/http';
const httpSubject = new Subject()
const http$ = httpSubject.asObservable()
const httpFactory = (backend, options) => {
const newHttp = new Http(backend, options);
const helperHttp = new Http(backend, options);
newHttp.get = function(strng, options) {
return helperHttp.get(strng, options).do((response) => {
setTimeout(() => {
httpSubject.next({});
}, 300);
});
};
return newHttp;
}
TestBed.configureTestingModule({
imports: [HttpModule, FormModule],
providers: [
UrlService,
ConnectionBackend,
BaseRequestOptions
{
provide: Http,
useFactory: httpFactory,
deps: [ XHRBackend, BaseRequestOptions]
}
],
declarations: [ReviewComponent]
})
it('should show review after the getReviewByProcess resolves',
async(() => {
http$.subscribe(() => {
expect(componentSpy).toHaveBeenCalled();
expect(component.Reviews[0]).not.toBe(undefined);
})
fixture.detectChanges();
}));
即使在放置适当的URL,选项等之后,您作为示例设置的代码也会出现调用堆栈错误。 – LandSharks
@LandSharks对不起,我已更新它,它在本地为我工作。给它一个去,如果你仍然有问题,我可以给你发送我创建的测试版本。 –
@LandSharks请评论,如果你设法尝试或找到另一种解决方案。这将是很好的知道。 –
我不想从我的web API返回模拟数据,而是合法的数据。这只是茉莉花不能支持的东西,我应该寻找使用量角器的解决方案? – LandSharks
这是单元测试,它应该被限定范围并专用于特定的组件/功能,并且您需要模拟环境来对其进行存档。如果你想模拟http,你可以使用MockBackend来模拟http服务级别。如果您需要给您打电话Web API以进行测试 - 使用Protractor进行端到端测试,但具有角度2单元测试的强大功能,我并不认为e2e没有太多需求。 –
茉莉花不支持集成测试吗?我注意到角度2的主要文档视域:https://angular.io/docs/ts/latest/guide/testing.html#!#component-with-async-service 他们正在做的确切的事情我想要,但缺少一个http连接。那里的数据是静态的。 – LandSharks