深入指南
測試

測試工具 API

NOTE: 本指南正陸續更新為 Vitest 版本,目前部分工具 API 的說明與範例仍以 Karma/Jasmine 的情境呈現。我們正積極提供 Vitest 的對等內容與更新後的指引。

本頁說明最實用的 Angular 測試功能。

Angular 測試工具包括 TestBed、ComponentFixture,以及一些控制測試環境的函式。 TestBed 與 ComponentFixture 類別另有專節說明。

以下是獨立函式的摘要,依可能的實用程度排序:

Function Details
[inject] 將目前 TestBed 注入器中的一或多個服務注入測試函式。它無法注入由元件本身提供的服務。請參閱 debugElement.injector 的討論。
getTestBed 取得 TestBed 的目前實例。通常不需要使用,因為 TestBed 類別的靜態類別方法一般就已足夠。TestBed 實例公開了一些較少使用、且沒有靜態方法版本的成員。

如需處理複雜的非同步情境,或測試舊版以 Zone.js 為基礎的應用程式,請參閱 Zone.js 測試工具 指南。

TestBed 類別摘要

TestBed 類別是主要的 Angular 測試工具之一。 它的 API 相當龐大,在你逐漸探索之前可能會覺得難以招架。 請先閱讀本指南前面的部分掌握基礎,再試著理解完整的 API。

傳入 configureTestingModule 的模組定義,是 @NgModule 中繼資料屬性的子集。

type TestModuleMetadata = {
  providers?: any[];
  declarations?: any[];
  imports?: any[];
  schemas?: Array<SchemaMetadata | any[]>;
};

每個覆寫方法都接受一個 MetadataOverride<T>,其中 T 是適合該方法的中繼資料種類,也就是 @NgModule、@Component、@Directive 或 @Pipe 的參數。

type MetadataOverride<T> = {
  add?: Partial<T>;
  remove?: Partial<T>;
  set?: Partial<T>;
};

TestBed API 由靜態類別方法組成,這些方法不是更新、就是引用 TestBed 的_全域_實例。

在內部,所有靜態方法都是目前執行期 TestBed 實例的方法的包裝,而該實例也會由 getTestBed() 函式傳回。

請在 beforeEach() _內_呼叫 TestBed 方法,確保每個個別測試開始前都是全新狀態。

以下是最重要的靜態方法,依可能的實用程度排序。

Methods Details
configureTestingModule 測試墊片會建立初始測試環境與預設的測試模組。預設測試模組設定了基本的宣告式成員,以及每位測試者都需要的一些 Angular 服務替代實作。
呼叫 configureTestingModule,可以加入與移除匯入、宣告(元件、指令與管道)及 providers,為特定的測試組調整測試模組設定。
compileComponents 在你完成測試模組的設定後,以非同步方式編譯該模組。如果測試模組的_任何_元件具有非同步載入的資源(例如 @defer 區塊),你必須呼叫這個方法。
createComponent<T> 根據目前的 TestBed 設定,建立型別為 T 的元件實例。呼叫 createComponent 之後,TestBed 設定會在目前規格的執行期間凍結。
overrideComponent 取代指定元件類別的中繼資料,該元件可能深藏在內層模組之中。
overrideDirective 取代指定指令類別的中繼資料,該指令可能深藏在內層模組之中。
overridePipe 取代指定管道類別的中繼資料,該管道可能深藏在內層模組之中。
overrideModule 取代指定的 NgModule。請記住,模組可以匯入其他模組。overrideModule 方法可以深入目前的測試模組,修改其中某個內層模組。
inject 從目前的 TestBed 注入器取得服務。inject 函式通常就足以勝任此用途。但若 inject 無法提供該服務,就會擲出錯誤。
如果該服務是選用的,該怎麼辦?
TestBed.inject() 方法接受一個選用的第二個參數,也就是 Angular 找不到 provider 時要傳回的物件(此例為 null):expect(TestBed.inject(NotProvided, null)).toBeNull(); 呼叫 TestBed.inject 之後,TestBed 設定會在目前規格的執行期間凍結。
initTestEnvironment 為整個測試執行初始化測試環境。
測試墊片會為你呼叫它,所以你幾乎不需要自己呼叫。
這個方法只能呼叫_恰好一次_。若要在測試執行中途變更這個預設值,請先呼叫 resetTestEnvironment。
指定一個預設的 Angular 測試模組與一個 PlatformRef。非瀏覽器平台的替代方案可在 @angular/platform-<platform_name>/testing 這個一般形式下取得。
resetTestEnvironment 重設初始測試環境,包括預設的測試模組。

少數 TestBed 實例方法沒有對應的靜態 TestBed _類別_方法。 這些方法很少需要使用。

ComponentFixture

TestBed.createComponent<T> 會建立元件 T 的實例,並為該元件傳回強型別的 ComponentFixture。

ComponentFixture 的屬性與方法提供對元件、其 DOM 表示方式,以及其 Angular 環境各方面的存取。

ComponentFixture 屬性

以下是對測試者最重要的屬性,依可能的實用程度排序。

Properties Details
componentInstance 由 TestBed.createComponent 建立的元件類別實例。
debugElement 與元件根元素相關聯的 DebugElement。
debugElement 讓你在測試與除錯期間深入了解元件及其 DOM 元素。這是測試者的關鍵屬性。最值得關注的成員在下文說明。
nativeElement 位於元件根部的原生 DOM 元素。
changeDetectorRef 元件宿主檢視的 ChangeDetectorRef,不是元件本身的。請參閱在 TestBed 中使用 Zoneless。

ComponentFixture 方法

fixture 方法會讓 Angular 對元件樹執行某些工作。 呼叫這些方法,可以觸發 Angular 行為以回應模擬的使用者動作。

以下是最實用的方法。

Methods Details
detectChanges 為元件觸發一個變更偵測週期。
呼叫它來初始化元件(它會呼叫 ngOnInit),以及在測試程式碼變更元件的資料繫結屬性值之後呼叫。Angular 無法得知你已變更 personComponent.name,在你呼叫 detectChanges 之前,不會更新 name 繫結。
之後會執行 checkNoChanges,確認沒有循環更新,除非以 detectChanges(false) 的方式呼叫;
autoDetectChanges 當你希望 fixture 自動偵測變更時,將它設為 true。
當自動偵測為 true 時,測試 fixture 會在建立元件後立即呼叫 detectChanges。接著它會監聽相關的 zone 事件,並相應地呼叫 detectChanges。當你的測試程式碼直接修改元件屬性值時,你可能仍然必須呼叫 fixture.detectChanges 來觸發資料繫結更新。
使用 zoneless 變更偵測時(TestBed 的預設值),預設為 true;使用以 Zone 為基礎的變更偵測時,預設為 false。
checkNoChanges 執行一次變更偵測,確保沒有待處理的變更。若有變更則擲出例外。
isStable 若 fixture 目前_穩定_,傳回 true。若有尚未完成的非同步工作,傳回 false。
whenStable 傳回一個在 fixture 穩定時完成的 promise。
若要在非同步活動或非同步變更偵測完成後繼續測試,請掛接該 promise。請參閱 whenStable。
destroy 觸發元件銷毀。

DebugElement

DebugElement 提供對元件 DOM 表示方式的關鍵洞察。

從測試根元件由 fixture.debugElement 傳回的 DebugElement,你可以走訪(並查詢)fixture 的整個元素與元件子樹。

以下是對測試者最實用的 DebugElement 成員,依大約的實用程度排序:

Members Details
nativeElement 瀏覽器中對應的 DOM 元素
query 呼叫 query(predicate: Predicate<DebugElement>),傳回子樹中任意深度第一個符合述詞的 DebugElement。
queryAll 呼叫 queryAll(predicate: Predicate<DebugElement>),傳回子樹中任意深度所有符合述詞的 DebugElement。
injector 宿主的相依性注入器。例如,根元素的元件實例注入器。
componentInstance 元素自身的元件實例(如果有的話)。
context 提供此元素父層上下文的物件。通常是掌管此元素的祖先元件實例。
當元素在 @for 區塊內重複時,其 context 是一個 RepeaterContext,其 $implicit 屬性是該列實例的值。例如 @for(hero of heroes; ...) 中的 hero。
children 直接的 DebugElement 子元素。透過 children 逐層往下走訪整棵樹。DebugElement 也有 childNodes,是 DebugNode 物件的清單。DebugElement 源自 DebugNode 物件,而且節點的數量往往比元素多。測試者通常可以忽略純節點。
parent DebugElement 的父元素。若這是根元素,則為 Null。
name 元素的標籤名稱(如果它是元素的話)。
triggerEventHandler 若元素的 listeners 集合中有對應的監聽器,則依名稱觸發該事件。第二個參數是處理常式預期的_事件物件_。
若事件缺少監聽器,或有其他問題,請考慮呼叫 nativeElement.dispatchEvent(eventObject)。
listeners 附加在元件 @Output 屬性及/或元素事件屬性上的回呼。
providerTokens 此元件的注入器查找權杖。包括元件本身,加上元件在其 providers 中繼資料中所列的權杖。
references 與範本區域變數相關聯的物件字典(例如 #foo),以區域變數名稱為鍵。

DebugElement.query(predicate) 與 DebugElement.queryAll(predicate) 方法接受一個述詞,用來篩選來源元素的子樹,找出符合的 DebugElement。

述詞可以是任何接受 DebugElement 並傳回_真值_的方法。 下列範例找出所有具有名為 "content" 的範本區域變數參照的 DebugElement:

// Filter for DebugElements with a #content reference
const contentRefs = el.queryAll((de) => de.references['content']);

Angular 的 By 類別有三個靜態方法,用於常見的述詞:

Static method Details
By.all 傳回所有元素
By.css(selector) 傳回符合 CSS 選擇器的元素
By.directive(directive) 傳回 Angular 對應到該指令類別實例的元素
// Can find DebugElement either by css selector or by directive
const h2 = fixture.debugElement.query(By.css('h2'));
const directive = fixture.debugElement.query(By.directive(Highlight));