W3Cschool
恭喜您成為首批注冊用戶
獲得88經(jīng)驗(yàn)值獎勵
如同所有的外部依賴一樣,你必須把 HTTP 后端也 Mock
掉,以便你的測試可以模擬這種與后端的互動。 @angular/common/http/testing
庫能讓這種 Mock
工作變得直截了當(dāng)。
Angular 的 HTTP 測試庫是專為其中的測試模式而設(shè)計(jì)的。在這種模式下,會首先在應(yīng)用中執(zhí)行代碼并發(fā)起請求。 然后,這個(gè)測試會期待發(fā)起或未發(fā)起過某個(gè)請求,并針對這些請求進(jìn)行斷言, 最終對每個(gè)所預(yù)期的請求進(jìn)行刷新(flush
)來對這些請求提供響應(yīng)。
最終,測試可能會驗(yàn)證這個(gè)應(yīng)用不曾發(fā)起過非預(yù)期的請求。
本章所講的這些測試位于 "src/testing/http-client.spec.ts" 中。 在 "src/app/heroes/heroes.service.spec.ts" 中還有一些測試,用于測試那些調(diào)用了 "HttpClient" 的數(shù)據(jù)服務(wù)。
要開始測試那些通過 HttpClient
發(fā)起的請求,就要導(dǎo)入 HttpClientTestingModule
模塊,并把它加到你的 TestBed
設(shè)置里去,代碼如下:
Path:"app/testing/http-client.spec.ts (imports)" 。
// Http testing module and mocking controller
import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';
// Other imports
import { TestBed } from '@angular/core/testing';
import { HttpClient, HttpErrorResponse } from '@angular/common/http';
然后把 HTTPClientTestingModule
添加到 TestBed
中,并繼續(xù)設(shè)置被測服務(wù)。
Path:"app/testing/http-client.spec.ts(setup)" 。
describe('HttpClient testing', () => {
let httpClient: HttpClient;
let httpTestingController: HttpTestingController;
beforeEach(() => {
TestBed.configureTestingModule({
imports: [ HttpClientTestingModule ]
});
// Inject the http service and test controller for each test
httpClient = TestBed.inject(HttpClient);
httpTestingController = TestBed.inject(HttpTestingController);
});
/// Tests begin ///
});
現(xiàn)在,在測試中發(fā)起的這些請求會發(fā)給這些測試用的后端(testing backend),而不是標(biāo)準(zhǔn)的后端。
這種設(shè)置還會調(diào)用 TestBed.inject()
,來獲取注入的 HttpClient
服務(wù)和模擬對象的控制器 HttpTestingController
,以便在測試期間引用它們。
現(xiàn)在,你就可以編寫測試,等待 GET
請求并給出模擬響應(yīng)。
Path:"app/testing/http-client.spec.ts(httpClient.get)" 。
it('can test HttpClient.get', () => {
const testData: Data = {name: 'Test Data'};
// Make an HTTP GET request
httpClient.get<Data>(testUrl)
.subscribe(data =>
// When observable resolves, result should match test data
expect(data).toEqual(testData)
);
// The following `expectOne()` will match the request's URL.
// If no requests or multiple requests matched that URL
// `expectOne()` would throw.
const req = httpTestingController.expectOne('/data');
// Assert that the request is a GET.
expect(req.request.method).toEqual('GET');
// Respond with mock data, causing Observable to resolve.
// Subscribe callback asserts that correct data was returned.
req.flush(testData);
// Finally, assert that there are no outstanding requests.
httpTestingController.verify();
});
最后一步,驗(yàn)證沒有發(fā)起過預(yù)期之外的請求,足夠通用,因此你可以把它移到 afterEach()
中:
afterEach(() => {
// After every test, assert that there are no more pending requests.
httpTestingController.verify();
});
如果僅根據(jù) URL 匹配還不夠,你還可以自行實(shí)現(xiàn)匹配函數(shù)。 比如,你可以驗(yàn)證外發(fā)的請求是否帶有某個(gè)認(rèn)證頭:
// Expect one request with an authorization header
const req = httpTestingController.expectOne(
req => req.headers.has('Authorization')
);
像前面的 expectOne()
測試一樣,如果零或兩個(gè)以上的請求滿足了這個(gè)斷言,它就會拋出異常。
如果你需要在測試中對重復(fù)的請求進(jìn)行響應(yīng),可以使用 match()
API 來代替 expectOne()
,它的參數(shù)不變,但會返回一個(gè)與這些請求相匹配的數(shù)組。一旦返回,這些請求就會從將來要匹配的列表中移除,你要自己驗(yàn)證和刷新(flush)它。
// get all pending requests that match the given URL
const requests = httpTestingController.match(testUrl);
expect(requests.length).toEqual(3);
// Respond to each request with different results
requests[0].flush([]);
requests[1].flush([testData[0]]);
requests[2].flush(testData);
你還要測試應(yīng)用對于 HTTP 請求失敗時(shí)的防護(hù)。
調(diào)用 request.flush()
并傳入一個(gè)錯誤信息,如下所示:
it('can test for 404 error', () => {
const emsg = 'deliberate 404 error';
httpClient.get<Data[]>(testUrl).subscribe(
data => fail('should have failed with the 404 error'),
(error: HttpErrorResponse) => {
expect(error.status).toEqual(404, 'status');
expect(error.error).toEqual(emsg, 'message');
}
);
const req = httpTestingController.expectOne(testUrl);
// Respond with mock error
req.flush(emsg, { status: 404, statusText: 'Not Found' });
});
另外,你還可以使用 ErrorEvent
來調(diào)用 request.error()
.
it('can test for network error', () => {
const emsg = 'simulated network error';
httpClient.get<Data[]>(testUrl).subscribe(
data => fail('should have failed with the network error'),
(error: HttpErrorResponse) => {
expect(error.error.message).toEqual(emsg, 'message');
}
);
const req = httpTestingController.expectOne(testUrl);
// Create mock ErrorEvent, raised when something goes wrong at the network level.
// Connection timeout, DNS error, offline, etc
const mockError = new ErrorEvent('Network error', {
message: emsg,
});
// Respond with mock error
req.error(mockError);
});
Copyright©2021 w3cschool編程獅|閩ICP備15016281號-3|閩公網(wǎng)安備35020302033924號
違法和不良信息舉報(bào)電話:173-0602-2364|舉報(bào)郵箱:jubao@eeedong.com
掃描二維碼
下載編程獅App
編程獅公眾號
聯(lián)系方式:
更多建議: