Docker 的優(yōu)點(diǎn)
635
2025-03-31
原文:Testing and faking Angular dependencies
依賴注入是 Angular 的一個(gè)關(guān)鍵特性。這種靈活的方法使我們的可聲明和基于類的服務(wù)更容易隔離測(cè)試。
可搖樹依賴項(xiàng)移除了間接層 即Angular 模塊,但我們?nèi)绾螠y(cè)試它們的可搖樹 provider?我們將測(cè)試依賴于特定平臺(tái) API 的注入令牌的值工廠。
某些組件具有特定于瀏覽器的功能。我們將一起測(cè)試通知用戶我們將終止 Internet Explorer 11 支持的橫幅。一個(gè)合適的測(cè)試套件可以給我們足夠的信心,我們甚至不必在 Internet Explorer 11 中測(cè)試橫幅。
我們必須小心不要對(duì)復(fù)雜的集成場(chǎng)景過(guò)于自信。我們應(yīng)該始終確保在盡可能接近生產(chǎn)的環(huán)境中執(zhí)行 QA(質(zhì)量保證)測(cè)試。這意味著在真實(shí) Internet Explorer 11 瀏覽器中運(yùn)行應(yīng)用程序。
Angular 測(cè)試實(shí)用程序使我們能夠偽造依賴項(xiàng)以進(jìn)行測(cè)試。我們將使用 Angular CLI 的測(cè)試框架 Jasmine 探索在 Angular 測(cè)試環(huán)境中配置和解決依賴關(guān)系的不同選項(xiàng)。
通過(guò)示例,我們將探索組件 fixtures、組件初始化、自定義 expectations、模擬事件。我們甚至?xí)榉浅>?jiǎn)但明確的測(cè)試用例創(chuàng)建自定義測(cè)試工具。
Faking dependency injection tokens used in token providers
看個(gè)例子。
我們創(chuàng)建了一個(gè)依賴注入令牌,該令牌評(píng)估為指示當(dāng)前瀏覽器是否為 Internet Explorer 11 的標(biāo)志。
// user-agent.token.ts import { InjectionToken } from '@angular/core'; export const userAgentToken: InjectionToken
// is-internet-explorer-11.token.ts import { inject, InjectionToken } from '@angular/core'; import { userAgentToken } from './user-agent.token'; export const isInternetExplorer11Token: InjectionToken
為了單獨(dú)測(cè)試 Internet Explorer 11 標(biāo)志提供程序,我們可以用一個(gè)假值替換 userAgentToken。
我們注意到用戶代理字符串提供程序從特定于平臺(tái)的 Navigator API 中提取相關(guān)信息。 為了學(xué)習(xí),假設(shè)我們將需要來(lái)自同一個(gè)全局導(dǎo)航器對(duì)象的其他信息。 根據(jù)我們使用的測(cè)試運(yùn)行器,Navigator API 甚至可能在測(cè)試環(huán)境中不可用。
為了能夠創(chuàng)建虛假的導(dǎo)航器配置,我們?yōu)閷?dǎo)航器 API 創(chuàng)建了一個(gè)依賴注入令牌。 我們可以在開發(fā)和測(cè)試期間使用這些虛假配置來(lái)模擬用戶上下文。
// user-agent.token.ts import { inject, InjectionToken } from '@angular/core'; import { navigatorToken } from './navigator.token'; export const userAgentToken: InjectionToken
// navigator.token.ts import { InjectionToken } from '@angular/core'; export const navigatorToken: InjectionToken
對(duì)于我們的第一個(gè)測(cè)試,我們將為 Navigator API 令牌提供一個(gè)假值,該令牌在工廠提供程序中用作用戶代理字符串令牌的依賴項(xiàng)。
為了出于測(cè)試目的替換令牌提供程序,我們?cè)?Angular 測(cè)試模塊中添加了一個(gè)覆蓋提供程序,類似于 Angular 模塊自己的提供程序如何覆蓋導(dǎo)入的 Angular 模塊的提供程序。
// navigator-api.spec.ts import { inject, TestBed } from '@angular/core/testing'; import { navigatorToken } from './navigator.token'; import { userAgentToken } from './user-agent.token'; describe('Navigator API', () => { describe('User agent string', () => { describe('Provider', () => { beforeEach(() => { TestBed.configureTestingModule({ providers: [ { provide: navigatorToken, useValue: { userAgent: 'Fake browser', }, }, ], }); }); it( 'extracts the user agent string from the Navigator API token', inject([userAgentToken], (userAgent: string) => { expect(userAgent).toBe('Fake browser'); })); }); }); });
請(qǐng)注意,雖然我們正在測(cè)試的是 user agent 令牌及其提供者,但我們正在用假值替換 navigator 令牌依賴項(xiàng)。
Resolving dependencies using the inject function
Angular 測(cè)試實(shí)用程序?yàn)槲覀兲峁┝瞬恢挂环N解決依賴關(guān)系的方法。 在這個(gè)測(cè)試中,我們使用@angular/core/testing 包中的 inject 函數(shù)(*不是@angular/core 中的那個(gè))。
注入函數(shù)允許我們通過(guò)在我們作為參數(shù)傳遞的數(shù)組中列出它們的標(biāo)記來(lái)解決多個(gè)依賴項(xiàng)。 每個(gè)依賴注入令牌都被解析并作為參數(shù)提供給測(cè)試用例函數(shù)。
例子:https://stackblitz.com/edit/testing-and-faking-angular-dependencies?file=src%2Fapp%2Finternet-explorer%2Finternet-explorer-11-banner.component.spec.ts
Gotchas when using the Angular testing function inject
當(dāng)我們使用沒(méi)有聲明的 Angular 測(cè)試模塊時(shí),即使在同一個(gè)測(cè)試用例中,我們通常也可以多次覆蓋 provider. 我們將在本文后面研究一個(gè)例子。
值得注意的是,在使用 Angular 測(cè)試功能 inject 時(shí),情況并非如此。 它在執(zhí)行測(cè)試用例函數(shù)體之前解決依賴關(guān)系。
我們可以使用靜態(tài)方法 TestBed.configureTestingModule 和 TestBed.overrideProvider 替換 beforeAll 和 beforeEach 鉤子中的令牌提供者。 但是當(dāng)我們使用注入測(cè)試功能來(lái)解決依賴關(guān)系時(shí),我們不能在測(cè)試用例之間改變提供者或在測(cè)試用例期間替換它。
在沒(méi)有 declarables 的測(cè)試中解決 Angular 依賴關(guān)系的一種更靈活的方法是使用靜態(tài)方法 TestBed.get。 我們只需從測(cè)試用例函數(shù)或測(cè)試生命周期鉤子的任何地方傳遞我們想要解析的依賴注入令牌。
讓我們看另一個(gè)原生瀏覽器 API 示例,我們使用依賴注入令牌對(duì)其進(jìn)行抽象,以進(jìn)行開發(fā)和測(cè)試。
Location 依賴于 Document:
// location.token.ts import { DOCUMENT } from '@angular/common'; import { inject, InjectionToken } from '@angular/core'; export const locationToken: InjectionToken
// location-api.spec.ts import { DOCUMENT } from '@angular/common'; import { TestBed } from '@angular/core/testing'; import { locationToken } from './location.token'; describe('Location API', () => { describe('Provider', () => { it('extracts the location from the DOCUMENT token', () => { TestBed.configureTestingModule({ providers: [ { provide: DOCUMENT, useValue: { location: { href: 'Fake URL', }, }, }, ], }); const location: Location = TestBed.get(locationToken); expect(location.href).toBe('Fake URL'); }); }); });
我們通過(guò)使用靜態(tài) TestBed.get 方法使 Angular 依賴注入系統(tǒng)解析 Location API。 正如 StackBlitz 測(cè)試項(xiàng)目中所證明的那樣,文檔令牌被成功偽造并用于使用其真實(shí)的工廠提供程序來(lái)解析被測(cè)令牌。
Gotchas when resolving dependencies using TestBed
在之前的測(cè)試中,我們通過(guò)在 Angular 測(cè)試模塊中為 DOCUMENT 令牌提供文檔來(lái)將文檔替換為假對(duì)象。 如果我們沒(méi)有這樣做,Angular 就會(huì)提供全局文檔對(duì)象。
此外,如果我們想測(cè)試不同的文檔配置,如果我們沒(méi)有為文檔令牌創(chuàng)建 test provider,我們將無(wú)法這樣做。
在我們使用 TestBed.configureTestingModule 添加測(cè)試提供程序的情況下,我們可以使用靜態(tài)方法 TestBed.overrideProvider 在各種測(cè)試用例中將其替換為不同的假值。 在測(cè)試 Internet Explorer 11 檢測(cè)和 Internet Explorer 11 橫幅組件時(shí),我們將使用此技術(shù)創(chuàng)建測(cè)試工具。
請(qǐng)注意,這是唯一可能的,因?yàn)槲覀儾皇褂?declarable。 一旦我們調(diào)用 TestBed.createComponent,Angular 測(cè)試平臺(tái)的依賴就被鎖定了。
Testing value factories with dependencies
在本文的第一部分中,我們介紹了一個(gè)在其提供程序中帶有值工廠的令牌。 值工廠評(píng)估用戶代理字符串是否代表 Internet Explorer 11 瀏覽器。
為了測(cè)試值工廠中的瀏覽器檢測(cè),我們從真實(shí)瀏覽器中收集了一些用戶代理字符串并將它們放在一個(gè)枚舉中。
// fake-user-agent.ts export enum FakeUserAgent { Chrome = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36', InternetExplorer10 = 'Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 10.0; WOW64; Trident/7.0; .NET4.0C; .NET4.0E; .NET CLR 2.0.50727; .NET CLR 3.0.30729; .NET CLR 3.5.30729)', InternetExplorer11 = 'Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; .NET4.0C; .NET4.0E; .NET CLR 2.0.50727; .NET CLR 3.0.30729; .NET CLR 3.5.30729; rv:11.0) like Gecko', Firefox = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:65.0) Gecko/20100101 Firefox/65.0', }
在 Internet Explorer 11 檢測(cè)測(cè)試套件中,我們將幾乎孤立地測(cè)試 isInternetExplorer11Token。 但真正的業(yè)務(wù)邏輯價(jià)值在于它的工廠提供者,它依賴于用戶代理令牌。
用戶代理令牌從 Navigator API 令牌中提取其值,但 Navigator API 測(cè)試套件已涵蓋該依賴項(xiàng)。 我們將選擇用戶代理令牌作為依賴鏈中合適的位置來(lái)開始偽造依賴。
// internet-explorer-11-detection.spec.ts import { TestBed } from '@angular/core/testing'; import { isInternetExplorer11Token } from './is-internet-explorer-11.token'; import { FakeUserAgent } from './fake-user-agent'; import { userAgentToken } from './user-agent.token'; describe('Internet Explorer 11 detection', () => { function setup({ userAgent }: { userAgent: string }) { TestBed.overrideProvider(userAgentToken, { useValue: userAgent }); return { isInternetExplorer11: TestBed.get(isInternetExplorer11Token), }; } const nonInternetExplorerUserAgents: ReadonlyArray
在指定測(cè)試用例之前,我們創(chuàng)建了一個(gè)測(cè)試設(shè)置函數(shù),并從我們的假用戶代理字符串中減少了一組非 Internet Explorer 用戶代理字符串。
測(cè)試設(shè)置函數(shù)采用用戶代理并使用它來(lái)偽造用戶代理令牌提供者。然后我們返回一個(gè)具有屬性 isInternetExplorer11 的對(duì)象,該對(duì)象具有通過(guò) TestBed.get 方法從 isInternetExplorer11Token 評(píng)估的值。
讓我們先測(cè)試一下快樂(lè)路徑。我們傳遞 Internet Explorer 11 用戶代理字符串,并期望被測(cè)令牌通過(guò) Angular 的依賴注入系統(tǒng)評(píng)估為 true。正如 StackBlitz 測(cè)試項(xiàng)目中所見,瀏覽器檢測(cè)按預(yù)期工作。
當(dāng)用戶使用 Internet Explorer 10 瀏覽時(shí)會(huì)發(fā)生什么?我們的測(cè)試套件表明 Internet Explorer 11 在這種情況下不會(huì)導(dǎo)致誤報(bào)。
換句話說(shuō),當(dāng)依賴令牌中提供 Internet Explorer 10 用戶代理字符串時(shí),被測(cè)令牌評(píng)估為 false。如果這不是預(yù)期用途,我們需要更改檢測(cè)邏輯。現(xiàn)在我們已經(jīng)進(jìn)行了測(cè)試,很容易證明該更改何時(shí)會(huì)成功。
最后的測(cè)試在 FakeUserAgent 枚舉定義的非 Internet Explorer 瀏覽器上執(zhí)行瀏覽器檢測(cè)。測(cè)試用例遍歷用戶代理字符串,偽造用戶代理提供程序,評(píng)估 isInternetExplorer11Token 并期望其值為 false。如果不是這種情況,測(cè)試運(yùn)行程序會(huì)顯示有用的錯(cuò)誤消息。
Faking dependencies in component tests
現(xiàn)在我們對(duì) Internet Explorer 11 瀏覽器檢測(cè)感到滿意,創(chuàng)建和顯示棄用橫幅很簡(jiǎn)單。
// internet-explorer-11-banner.component.ts import { Component, Inject } from '@angular/core'; import { isInternetExplorer11Token } from './is-internet-explorer-11.token'; @Component({ selector: 'internet-explorer-11-banner', templateUrl: './internet-explorer-11-banner.component.html', }) export class InternetExplorer11BannerComponent { private isDismissed = false; get isBannerVisible() { return this.isInternetExplorer11 && !this.isDismissed; } constructor( @Inject(isInternetExplorer11Token) private isInternetExplorer11: boolean, ) {} onDismiss() { this.isDismissed = true; } }
解除狀態(tài)只是作為本地 UI 狀態(tài)存儲(chǔ)在私有組件屬性中,該屬性由計(jì)算屬性 isBannerVisible 使用。
橫幅組件有一個(gè)依賴項(xiàng)——isInternetExplorer11Token,它被評(píng)估為一個(gè)布爾值。 由于 Inject 裝飾器,這個(gè)布爾值是通過(guò)橫幅組件構(gòu)造函數(shù)注入的。
Summary
在本文中,我們演示了如何在 Angular 項(xiàng)目中測(cè)試和偽造 tree-shakable 依賴項(xiàng)。 我們還測(cè)試了依賴于平臺(tái)特定 API 的價(jià)值工廠。
在此過(guò)程中,我們調(diào)查了使用注入測(cè)試功能解決依賴項(xiàng)時(shí)的問(wèn)題。 使用 TestBed,我們解決了依賴注入令牌并探索了這種方法的陷阱。
我們以多種方式測(cè)試了 Internet Explorer 11 棄用橫幅,以至于幾乎不需要在實(shí)際瀏覽器中對(duì)其進(jìn)行測(cè)試。 我們?cè)谒慕M件測(cè)試套件中偽造了它的依賴項(xiàng),但正如我們所討論的,我們應(yīng)該始終在復(fù)雜的集成場(chǎng)景的真實(shí)瀏覽器目標(biāo)中測(cè)試它。
Angular API
版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實(shí)的內(nèi)容,請(qǐng)聯(lián)系我們jiasou666@gmail.com 處理,核實(shí)后本網(wǎng)站將在24小時(shí)內(nèi)刪除侵權(quán)內(nèi)容。
版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實(shí)的內(nèi)容,請(qǐng)聯(lián)系我們jiasou666@gmail.com 處理,核實(shí)后本網(wǎng)站將在24小時(shí)內(nèi)刪除侵權(quán)內(nèi)容。