要模擬Http,您需要在中配置MockBackend
與Http
提供程序。然後,你可以訂閱其連接的提供模擬響應
beforeEach(() => {
TestBed.configureTestingModule({
providers: [
{
provide: Http, useFactory: (backend, options) => {
return new Http(backend, options);
},
deps: [MockBackend, BaseRequestOptions]
},
MockBackend,
BaseRequestOptions
]
});
});
我如何將考驗ngOnInit()函數?
問題是Http.get
調用的異步性質。當您調用fixture.detectChanges()
時,將調用ngOnInit
,但Http的異步特性會導致測試在Http完成之前運行。爲此,我們使用fakeAsync
,如here所述,但接下來的問題是您不能使用fakeAsync
和templateUrl
。你可以用setTimeout
來攻擊它並在那裏測試。這將工作。我個人雖然不喜歡它。
it('', async(() => {
setTimeout(() => {
// expectations here
}, 100);
})
就我個人而言,我認爲你有一個設計缺陷開始。應該將Http
調用抽象爲服務,並且組件應該與服務交互,而不是直接與Http
進行交互。如果您將設計更改爲(我會推薦),那麼您可以測試服務like in this example,並且對於組件測試,創建一個同步模擬,如this post中所述。
這裏是我會怎麼做
import { Component, OnInit, OnDestroy, DebugElement, Injectable } from '@angular/core';
import { CommonModule } from '@angular/common';
import { By } from '@angular/platform-browser';
import { Http, BaseRequestOptions, Response, ResponseOptions } from '@angular/http';
import { MockBackend, MockConnection } from '@angular/http/testing';
import { async, fakeAsync, inject, TestBed } from '@angular/core/testing';
import { Observable } from 'rxjs/Observable';
import { Subscription } from 'rxjs/Subscription';
@Injectable()
class BooksService {
constructor(private http: Http) {}
getBooks(): Observable<string[]> {
return this.http.get('')
.map(res => res.json() as string[]);
}
}
class MockBooksService {
subscription: Subscription;
content;
error;
constructor() {
this.subscription = new Subscription();
spyOn(this.subscription, 'unsubscribe');
}
getBooks() {
return this;
}
subscribe(next, error) {
if (this.content && next && !error) {
next(this.content);
}
if (this.error) {
error(this.error);
}
return this.subscription;
}
}
@Component({
template: `
<h4 *ngFor="let book of books">{{ book }}</h4>
`
})
class TestComponent implements OnInit, OnDestroy {
books: string[];
subscription: Subscription;
constructor(private service: BooksService) {}
ngOnInit() {
this.subscription = this.service.getBooks().subscribe(books => {
this.books = books;
});
}
ngOnDestroy() {
this.subscription.unsubscribe();
}
}
describe('component: TestComponent',() => {
let mockService: MockBooksService;
beforeEach(() => {
mockService = new MockBooksService();
TestBed.configureTestingModule({
imports: [ CommonModule ],
declarations: [ TestComponent ],
providers: [
{ provide: BooksService, useValue: mockService }
]
});
});
it('should set the books',() => {
mockService.content = ['Book1', 'Book2'];
let fixture = TestBed.createComponent(TestComponent);
fixture.detectChanges();
let debugEls: DebugElement[] = fixture.debugElement.queryAll(By.css('h4'));
expect(debugEls[0].nativeElement.innerHTML).toEqual('Book1');
expect(debugEls[1].nativeElement.innerHTML).toEqual('Book2');
});
it('should unsubscribe when destroyed',() => {
let fixture = TestBed.createComponent(TestComponent);
fixture.detectChanges();
fixture.destroy();
expect(mockService.subscription.unsubscribe).toHaveBeenCalled();
});
});
describe('service: BooksService',() => {
beforeEach(() => {
TestBed.configureTestingModule({
providers: [
{
provide: Http, useFactory: (backend, options) => {
return new Http(backend, options);
},
deps: [MockBackend, BaseRequestOptions]
},
MockBackend,
BaseRequestOptions,
BooksService
]
});
});
it('should return mocked content',
async(inject([MockBackend, BooksService],
(backend: MockBackend, service: BooksService) => {
backend.connections.subscribe((conn: MockConnection) => {
let ops = new ResponseOptions({body: '["Book1", "Book2"]'});
conn.mockRespond(new Response(ops));
});
service.getBooks().subscribe(books => {
expect(books[0]).toEqual('Book1');
expect(books[1]).toEqual('Book2');
});
})));
});
你是什麼意思測試?你想檢查它是否被調用,或者是什麼? –
好問題。我想提供一個模擬響應,並期望'BookListComponent'的'books'屬性被設置爲模擬響應的值。 –