Angular 依赖的测试和 Fake
原文:Testing and faking Angular dependencies
依賴注入是 Angular 的一個關鍵特性。這種靈活的方法使我們的可聲明和基于類的服務更容易隔離測試。
可搖樹依賴項移除了間接層 即Angular 模塊,但我們如何測試它們的可搖樹 provider?我們將測試依賴于特定平臺 API 的注入令牌的值工廠。
某些組件具有特定于瀏覽器的功能。我們將一起測試通知用戶我們將終止 Internet Explorer 11 支持的橫幅。一個合適的測試套件可以給我們足夠的信心,我們甚至不必在 Internet Explorer 11 中測試橫幅。
我們必須小心不要對復雜的集成場景過于自信。我們應該始終確保在盡可能接近生產的環境中執行 QA(質量保證)測試。這意味著在真實 Internet Explorer 11 瀏覽器中運行應用程序。
Angular 測試實用程序使我們能夠偽造依賴項以進行測試。我們將使用 Angular CLI 的測試框架 Jasmine 探索在 Angular 測試環境中配置和解決依賴關系的不同選項。
通過示例,我們將探索組件 fixtures、組件初始化、自定義 expectations、模擬事件。我們甚至會為非常精簡但明確的測試用例創建自定義測試工具。
Faking dependency injection tokens used in token providers
看個例子。
我們創建了一個依賴注入令牌,該令牌評估為指示當前瀏覽器是否為 Internet Explorer 11 的標志。
// user-agent.token.ts import { InjectionToken } from '@angular/core';export const userAgentToken: InjectionToken<string> =new InjectionToken('User agent string', {factory: (): string => navigator.userAgent,providedIn: 'root',}); // is-internet-explorer-11.token.ts import { inject, InjectionToken } from '@angular/core';import { userAgentToken } from './user-agent.token';export const isInternetExplorer11Token: InjectionToken<boolean> =new InjectionToken('Internet Explorer 11 flag', {factory: (): boolean =>/Trident\/7\.0.+rv:11\.0/.test(inject(userAgentToken)),providedIn: 'root',});為了單獨測試 Internet Explorer 11 標志提供程序,我們可以用一個假值替換 userAgentToken。
我們注意到用戶代理字符串提供程序從特定于平臺的 Navigator API 中提取相關信息。 為了學習,假設我們將需要來自同一個全局導航器對象的其他信息。 根據我們使用的測試運行器,Navigator API 甚至可能在測試環境中不可用。
為了能夠創建虛假的導航器配置,我們為導航器 API 創建了一個依賴注入令牌。 我們可以在開發和測試期間使用這些虛假配置來模擬用戶上下文。
// user-agent.token.ts import { inject, InjectionToken } from '@angular/core';import { navigatorToken } from './navigator.token';export const userAgentToken: InjectionToken<string> =new InjectionToken('User agent string', {factory: (): string => inject(navigatorToken).userAgent,providedIn: 'root',}); // navigator.token.ts import { InjectionToken } from '@angular/core';export const navigatorToken: InjectionToken<Navigator> =new InjectionToken('Navigator API', {factory: (): Navigator => navigator,providedIn: 'root',});對于我們的第一個測試,我們將為 Navigator API 令牌提供一個假值,該令牌在工廠提供程序中用作用戶代理字符串令牌的依賴項。
為了出于測試目的替換令牌提供程序,我們在 Angular 測試模塊中添加了一個覆蓋提供程序,類似于 Angular 模塊自己的提供程序如何覆蓋導入的 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');}));});}); });請注意,雖然我們正在測試的是 user agent 令牌及其提供者,但我們正在用假值替換 navigator 令牌依賴項。
Resolving dependencies using the inject function
Angular 測試實用程序為我們提供了不止一種解決依賴關系的方法。 在這個測試中,我們使用@angular/core/testing 包中的 inject 函數(*不是@angular/core 中的那個)。
注入函數允許我們通過在我們作為參數傳遞的數組中列出它們的標記來解決多個依賴項。 每個依賴注入令牌都被解析并作為參數提供給測試用例函數。
例子: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
當我們使用沒有聲明的 Angular 測試模塊時,即使在同一個測試用例中,我們通常也可以多次覆蓋 provider. 我們將在本文后面研究一個例子。
值得注意的是,在使用 Angular 測試功能 inject 時,情況并非如此。 它在執行測試用例函數體之前解決依賴關系。
我們可以使用靜態方法 TestBed.configureTestingModule 和 TestBed.overrideProvider 替換 beforeAll 和 beforeEach 鉤子中的令牌提供者。 但是當我們使用注入測試功能來解決依賴關系時,我們不能在測試用例之間改變提供者或在測試用例期間替換它。
在沒有 declarables 的測試中解決 Angular 依賴關系的一種更靈活的方法是使用靜態方法 TestBed.get。 我們只需從測試用例函數或測試生命周期鉤子的任何地方傳遞我們想要解析的依賴注入令牌。
讓我們看另一個原生瀏覽器 API 示例,我們使用依賴注入令牌對其進行抽象,以進行開發和測試。
Location 依賴于 Document:
// location.token.ts import { DOCUMENT } from '@angular/common'; import { inject, InjectionToken } from '@angular/core';export const locationToken: InjectionToken<Location> =new InjectionToken('Location API', {factory: (): Location => inject(DOCUMENT).location,providedIn: 'root',}); // 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');});}); });我們通過使用靜態 TestBed.get 方法使 Angular 依賴注入系統解析 Location API。 正如 StackBlitz 測試項目中所證明的那樣,文檔令牌被成功偽造并用于使用其真實的工廠提供程序來解析被測令牌。
Gotchas when resolving dependencies using TestBed
在之前的測試中,我們通過在 Angular 測試模塊中為 DOCUMENT 令牌提供文檔來將文檔替換為假對象。 如果我們沒有這樣做,Angular 就會提供全局文檔對象。
此外,如果我們想測試不同的文檔配置,如果我們沒有為文檔令牌創建 test provider,我們將無法這樣做。
在我們使用 TestBed.configureTestingModule 添加測試提供程序的情況下,我們可以使用靜態方法 TestBed.overrideProvider 在各種測試用例中將其替換為不同的假值。 在測試 Internet Explorer 11 檢測和 Internet Explorer 11 橫幅組件時,我們將使用此技術創建測試工具。
請注意,這是唯一可能的,因為我們不使用 declarable。 一旦我們調用 TestBed.createComponent,Angular 測試平臺的依賴就被鎖定了。
Testing value factories with dependencies
在本文的第一部分中,我們介紹了一個在其提供程序中帶有值工廠的令牌。 值工廠評估用戶代理字符串是否代表 Internet Explorer 11 瀏覽器。
為了測試值工廠中的瀏覽器檢測,我們從真實瀏覽器中收集了一些用戶代理字符串并將它們放在一個枚舉中。
// 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 檢測測試套件中,我們將幾乎孤立地測試 isInternetExplorer11Token。 但真正的業務邏輯價值在于它的工廠提供者,它依賴于用戶代理令牌。
用戶代理令牌從 Navigator API 令牌中提取其值,但 Navigator API 測試套件已涵蓋該依賴項。 我們將選擇用戶代理令牌作為依賴鏈中合適的位置來開始偽造依賴。
// 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<string> =Object.entries(FakeUserAgent).filter(([browser]) =>!browser.toLowerCase().includes('internetexplorer')).map(([_browser, userAgent]) => userAgent);it('accepts an Internet Explorer 11 user agent', () => {const { isInternetExplorer11 } = setup({userAgent: FakeUserAgent.InternetExplorer11,});expect(isInternetExplorer11).toBe(true);});it('rejects an Internet Explorer 10 user agent', () => {const { isInternetExplorer11 } = setup({userAgent: FakeUserAgent.InternetExplorer10,});expect(isInternetExplorer11).toBe(false);});it('rejects other user agents', () => {nonInternetExplorerUserAgents.forEach(userAgent => {const { isInternetExplorer11 } = setup({ userAgent });expect(isInternetExplorer11).toBe(false,`Expected to reject user agent: "${userAgent}"`);});}); });在指定測試用例之前,我們創建了一個測試設置函數,并從我們的假用戶代理字符串中減少了一組非 Internet Explorer 用戶代理字符串。
測試設置函數采用用戶代理并使用它來偽造用戶代理令牌提供者。然后我們返回一個具有屬性 isInternetExplorer11 的對象,該對象具有通過 TestBed.get 方法從 isInternetExplorer11Token 評估的值。
讓我們先測試一下快樂路徑。我們傳遞 Internet Explorer 11 用戶代理字符串,并期望被測令牌通過 Angular 的依賴注入系統評估為 true。正如 StackBlitz 測試項目中所見,瀏覽器檢測按預期工作。
當用戶使用 Internet Explorer 10 瀏覽時會發生什么?我們的測試套件表明 Internet Explorer 11 在這種情況下不會導致誤報。
換句話說,當依賴令牌中提供 Internet Explorer 10 用戶代理字符串時,被測令牌評估為 false。如果這不是預期用途,我們需要更改檢測邏輯。現在我們已經進行了測試,很容易證明該更改何時會成功。
最后的測試在 FakeUserAgent 枚舉定義的非 Internet Explorer 瀏覽器上執行瀏覽器檢測。測試用例遍歷用戶代理字符串,偽造用戶代理提供程序,評估 isInternetExplorer11Token 并期望其值為 false。如果不是這種情況,測試運行程序會顯示有用的錯誤消息。
Faking dependencies in component tests
現在我們對 Internet Explorer 11 瀏覽器檢測感到滿意,創建和顯示棄用橫幅很簡單。
<!-- internet-explorer-11-banner.component.html --> <aside *ngIf="isBannerVisible">Sorry, we will not continue to support Internet Explorer 11.<br />Please upgrade to Microsoft Edge.<br /><button (click)="onDismiss()">Dismiss</button> </aside> // 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;} }解除狀態只是作為本地 UI 狀態存儲在私有組件屬性中,該屬性由計算屬性 isBannerVisible 使用。
橫幅組件有一個依賴項——isInternetExplorer11Token,它被評估為一個布爾值。 由于 Inject 裝飾器,這個布爾值是通過橫幅組件構造函數注入的。
Summary
在本文中,我們演示了如何在 Angular 項目中測試和偽造 tree-shakable 依賴項。 我們還測試了依賴于平臺特定 API 的價值工廠。
在此過程中,我們調查了使用注入測試功能解決依賴項時的問題。 使用 TestBed,我們解決了依賴注入令牌并探索了這種方法的陷阱。
我們以多種方式測試了 Internet Explorer 11 棄用橫幅,以至于幾乎不需要在實際瀏覽器中對其進行測試。 我們在它的組件測試套件中偽造了它的依賴項,但正如我們所討論的,我們應該始終在復雜的集成場景的真實瀏覽器目標中測試它。
總結
以上是生活随笔為你收集整理的Angular 依赖的测试和 Fake的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 深圳可转债涨停规则
- 下一篇: jquery的发展由来和深入理解(一)