debugElement.query vs nativeElement.querySelector
在 Angular Jasmine 測試中,有兩種主要的 DOM 查詢方法,各有不同的使用時機和優勢。
debugElement.query
語法
const element = fixture.debugElement.query(By.css('selector'));
const elements = fixture.debugElement.queryAll(By.css('selector'));
優點
支援 Angular 特定選擇器 -
By.directive(Component)
跨平台兼容 - 適用於非瀏覽器環境
Angular 開發者友善 - 提供 Angular 特定功能
適用時機
查找 Angular 元件
// ✅ 推薦:查找 Angular 元件
const toggleComponent = fixture.debugElement.query(By.css('app-slide-toggle'));
const matIcon = fixture.debugElement.query(By.css('mat-icon'));
使用 Angular 特定選擇器
// ✅ 推薦:使用指令選擇器
const directive = fixture.debugElement.query(By.directive(MyDirective));
// ✅ 推薦:查找有特定指令的元素
const tooltipElement = fixture.debugElement.query(By.directive(MatTooltip));
需要跨平台支援
// ✅ 推薦:當測試需要在非瀏覽器環境執行時
const component = fixture.debugElement.query(By.css('app-component'));
nativeElement.querySelector
語法
const element = fixture.nativeElement.querySelector('selector');
const elements = fixture.nativeElement.querySelectorAll('selector');
優點
原生 DOM API - 熟悉的瀏覽器 API
性能較好 - 直接操作 DOM
支援複雜選擇器 - CSS 選擇器功能完整
適用時機
查找普通 HTML 元素
// ✅ 推薦:查找普通 HTML 元素
const button = fixture.nativeElement.querySelector('button');
const div = fixture.nativeElement.querySelector('div');
const span = fixture.nativeElement.querySelector('span');
使用 CSS class 或 ID
// ✅ 推薦:使用 CSS class
const warningIcon = fixture.nativeElement.querySelector('.warning-icon');
const errorMessage = fixture.nativeElement.querySelector('.error-message');
// ✅ 推薦:使用 ID
const header = fixture.nativeElement.querySelector('#main-header');
需要複雜選擇器
// ✅ 推薦:複雜的 CSS 選擇器
const element = fixture.nativeElement.querySelector('div.class:nth-child(2)');
const tableCell = fixture.nativeElement.querySelector('table tbody tr:first-child td');
// ✅ 推薦:多重選擇器
const elements = fixture.nativeElement.querySelectorAll('thead th');
實務範例
測試條件渲染
it('should render group by header for non-CSV data source', () => {
fixture.componentRef.setInput('isDataFromCsv', false);
fixture.detectChanges();
// Angular 元件 - 用 debugElement.query
const groupByToggle = fixture.debugElement.query(By.css('app-slide-toggle'));
expect(groupByToggle).not.toBeNull();
});
it('should display warning icon when needed', () => {
// 設定顯示警告的條件
component.groupByState.set({ isEnabled: true, columnsId: [], rule: {} });
fixture.detectChanges();
// CSS class - 用 nativeElement.querySelector
const warningIcon = fixture.nativeElement.querySelector('.warning-icon');
expect(warningIcon).not.toBeNull();
});
測試表格結構
it('should render all data columns as table rows', () => {
const testData = [
{ id: '1', columnName: 'Column 1', type: 'X', genericDataType: 'Numeric' },
{ id: '2', columnName: 'Column 2', type: 'Y', genericDataType: 'Varchar' }
];
fixture.componentRef.setInput('dataColumns', testData);
fixture.detectChanges();
// 查找表格列 - 用 nativeElement.querySelectorAll
const tableRows = fixture.nativeElement.querySelectorAll('tbody tr');
expect(tableRows.length).toBe(2);
});
測試元件互動
it('should emit event when icon button is clicked', () => {
spyOn(component.removeColumn, 'emit');
// 查找 Angular 元件 - 用 debugElement.query
const iconBtn = fixture.debugElement.query(By.css('app-icon-btn'));
// 觸發事件
iconBtn.triggerEventHandler('btnClickOutput', 'test-id');
expect(component.removeColumn.emit).toHaveBeenCalledWith('test-id');
});
選擇指南
使用 debugElement.query 當:
查找 Angular 元件 (
app-*
,mat-*
)需要使用 By.directive() 選擇器
測試需要 跨平台支援
使用 nativeElement.querySelector 當:
查找 普通 HTML 元素 (
div
,span
,button
)使用 CSS class 或 ID (
.class
,#id
)需要 複雜的 CSS 選擇器
重視 查詢性能
記憶口訣
「Angular 的用 debugElement,HTML 的用 nativeElement」
這樣的分工讓測試程式碼更清晰,也更容易維護!
Last updated