測試中的 DOM 元素檢查
在 Angular 應用的單元測試和整合測試中,我們經常需要檢查特定元素是否存在於 DOM 中、使用不同的選擇器查詢元素、以及驗證元素的樣式和內容。本篇筆記將詳細介紹這些 DOM 元素檢查技巧。
檢查元素是否存在於 DOM
在 Angular 測試中,我們需要頻繁地檢查某個元素是否存在於 DOM 中。這類檢查對於條件渲染的元素尤為重要。
檢查元素存在
it('應顯示標題元素', () => {
fixture.detectChanges();
// 方法 1:使用 fixture.debugElement
const titleElement = fixture.debugElement.query(By.css('.title'));
expect(titleElement).toBeTruthy();
// 方法 2:使用 nativeElement
const titleNative = fixture.nativeElement.querySelector('.title');
expect(titleNative).not.toBeNull();
});
檢查元素不存在
it('錯誤訊息應該不顯示', () => {
fixture.detectChanges();
// 方法 1:使用 fixture.debugElement
const errorElement = fixture.debugElement.query(By.css('.error-message'));
expect(errorElement).toBeNull();
// 方法 2:使用 nativeElement
const errorNative = fixture.nativeElement.querySelector('.error-message');
expect(errorNative).toBeNull();
});
檢查多個元素
it('應顯示 3 個選項', () => {
fixture.detectChanges();
// 方法 1:使用 queryAll
const options = fixture.debugElement.queryAll(By.css('.option'));
expect(options.length).toBe(3);
// 方法 2:使用 nativeElement
const optionsNative = fixture.nativeElement.querySelectorAll('.option');
expect(optionsNative.length).toBe(3);
});
檢查是否可見
元素存在於 DOM 中不代表它是可見的。有時我們還需要檢查元素是否實際顯示:
it('tooltip 應在 hover 時可見', () => {
// 觸發 hover 事件
const button = fixture.debugElement.query(By.css('.info-button'));
button.triggerEventHandler('mouseenter', {});
fixture.detectChanges();
// 檢查 tooltip 是否可見
const tooltip = fixture.debugElement.query(By.css('.tooltip'));
// 1. 檢查 display 屬性
const display = window.getComputedStyle(tooltip.nativeElement).display;
expect(display).not.toBe('none');
// 2. 檢查可見性類
expect(tooltip.nativeElement.classList).toContain('visible');
});
CSS 選擇器查詢技巧
在 Angular 測試中,我們可以使用不同的 CSS 選擇器來精確定位需要測試的元素。
基本選擇器
// 通過元素類型
const buttons = fixture.debugElement.queryAll(By.css('button'));
// 通過 CSS 類
const errorMessage = fixture.debugElement.query(By.css('.error-message'));
// 通過 ID
const userProfile = fixture.debugElement.query(By.css('#user-profile'));
// 通過屬性
const requiredInputs = fixture.debugElement.queryAll(By.css('[required]'));
複合選擇器
// 後代選擇器
const cardTitle = fixture.debugElement.query(By.css('.card .title'));
// 子元素選擇器
const listItems = fixture.debugElement.queryAll(By.css('ul > li'));
// 相鄰兄弟選擇器
const labelNextToInput = fixture.debugElement.query(By.css('input + label'));
// 屬性選擇器
const emailInput = fixture.debugElement.query(By.css('input[type="email"]'));
特殊選擇器技巧
// 包含特定文本的元素
const submitButton = fixture.debugElement.query(By.css('button:contains("提交")'));
// 第 n 個子元素
const secondListItem = fixture.debugElement.query(By.css('li:nth-child(2)'));
// 符合多個條件的元素
const activeCheckbox = fixture.debugElement.query(By.css('input[type="checkbox"].active:checked'));
使用 By.directive 通過指令查詢
除了 CSS 選擇器外,我們還可以通過 Angular 指令查詢元素:
import { MatButton } from '@angular/material/button';
// 查詢所有使用 MatButton 指令的元素
const matButtons = fixture.debugElement.queryAll(By.directive(MatButton));
檢查元素的樣式和 CSS 類
除了檢查元素是否存在,我們通常還需要驗證元素的樣式和 CSS 類是否符合預期。
檢查 CSS 類
it('錯誤狀態應該添加 error 類', () => {
// 設置錯誤狀態
component.hasError = true;
fixture.detectChanges();
const inputElement = fixture.debugElement.query(By.css('input')).nativeElement;
// 檢查是否包含特定類
expect(inputElement.classList).toContain('error');
// 檢查是否包含多個類
expect(inputElement.classList).toContain('form-control');
expect(inputElement.classList).toContain('invalid');
// 也可以這樣檢查
expect(inputElement.className).toContain('error');
});
檢查行內樣式
it('應該設置正確的行內樣式', () => {
component.highlightColor = 'red';
fixture.detectChanges();
const highlightedElement = fixture.debugElement.query(By.css('.highlight')).nativeElement;
// 檢查行內樣式
expect(highlightedElement.style.color).toBe('red');
expect(highlightedElement.style.fontWeight).toBe('bold');
});
檢查計算樣式
有時我們需要檢查計算後的樣式,而不只是行內樣式:
it('元素應該有正確的計算樣式', () => {
fixture.detectChanges();
const element = fixture.debugElement.query(By.css('.styled-element')).nativeElement;
const computedStyle = window.getComputedStyle(element);
expect(computedStyle.display).toBe('flex');
expect(computedStyle.marginTop).toBe('10px');
});
檢查 CSS 偽元素的內容
CSS 偽元素(如 :before
和 :after
)的內容檢查較為特殊,需要使用 getComputedStyle
和 getPropertyValue
:
it('必填欄位應顯示星號', () => {
fixture.detectChanges();
const requiredLabel = fixture.debugElement.query(By.css('.required-field')).nativeElement;
// 獲取 :after 偽元素的 content 屬性
const afterContent = window.getComputedStyle(requiredLabel, ':after')
.getPropertyValue('content');
// 檢查內容是否為星號
// 注意:content 值通常帶有引號,如 '"*"'
expect(afterContent).toBe('"*"');
});
實用測試案例集
以下是一些實際的測試案例,展示如何在 Angular 測試中檢查 DOM 元素。
案例 1: 測試條件渲染
it('當 searchable 為 true 時應顯示搜尋框', () => {
// 初始狀態
fixture.detectChanges();
let searchBox = fixture.debugElement.query(By.css('.search-input'));
expect(searchBox).toBeNull(); // 預設不顯示
// 設置為可搜尋
component.searchable = true;
fixture.detectChanges();
searchBox = fixture.debugElement.query(By.css('.search-input'));
expect(searchBox).toBeTruthy(); // 現在應該顯示
});
案例 2: 測試互動後的 DOM 變化
it('點擊切換按鈕應該顯示/隱藏內容', () => {
fixture.detectChanges();
// 初始狀態:內容應該隱藏
let content = fixture.debugElement.query(By.css('.toggle-content'));
expect(content).toBeNull();
// 點擊切換按鈕
const toggleButton = fixture.debugElement.query(By.css('.toggle-btn'));
toggleButton.nativeElement.click();
fixture.detectChanges();
// 現在內容應該顯示
content = fixture.debugElement.query(By.css('.toggle-content'));
expect(content).toBeTruthy();
// 再次點擊
toggleButton.nativeElement.click();
fixture.detectChanges();
// 內容應再次隱藏
content = fixture.debugElement.query(By.css('.toggle-content'));
expect(content).toBeNull();
});
案例 3: 測試表單驗證錯誤訊息
it('當表單欄位無效時應顯示錯誤訊息', () => {
fixture.detectChanges();
// 獲取輸入欄位和錯誤訊息元素
const emailInput = fixture.debugElement.query(By.css('input[formControlName="email"]'));
// 初始狀態:應該無錯誤
let errorMessage = fixture.debugElement.query(By.css('.email-error'));
expect(errorMessage).toBeNull();
// 設置無效的電子郵件
emailInput.nativeElement.value = 'invalid-email';
emailInput.nativeElement.dispatchEvent(new Event('input'));
fixture.detectChanges();
// 驗證錯誤訊息顯示
errorMessage = fixture.debugElement.query(By.css('.email-error'));
expect(errorMessage).toBeTruthy();
expect(errorMessage.nativeElement.textContent).toContain('請輸入有效的電子郵件');
// 修正為有效的電子郵件
emailInput.nativeElement.value = 'valid@example.com';
emailInput.nativeElement.dispatchEvent(new Event('input'));
fixture.detectChanges();
// 錯誤訊息應該消失
errorMessage = fixture.debugElement.query(By.css('.email-error'));
expect(errorMessage).toBeNull();
});
案例 4: 測試動態加載的下拉菜單 (Material Select)
it('點擊 mat-select 後應顯示可選項', async () => {
fixture.detectChanges();
// 初始狀態:選項面板不存在於 DOM
let optionsPanel = document.querySelector('.mat-select-panel');
expect(optionsPanel).toBeNull();
// 點擊 select 以展開選項
const select = fixture.debugElement.query(By.css('mat-select')).nativeElement;
select.click();
fixture.detectChanges();
// 等待面板渲染
await fixture.whenStable();
// 現在應該能找到選項面板
optionsPanel = document.querySelector('.mat-select-panel');
expect(optionsPanel).toBeTruthy();
// 檢查選項數量
const options = document.querySelectorAll('.mat-option');
expect(options.length).toBe(3); // 假設有 3 個選項
});
通過掌握這些 DOM 元素檢查技巧,你可以編寫更穩健、更全面的 Angular 測試,確保你的應用在各種情況下都能按預期運作。
Last updated