Angular Testing 筆記 --- 8 測試 Directive 與 Component 結合。

這次我們來測試 angular directive 元件。一樣我們要先產生一個 directive 這邊我們會使用 angular 官方的教學文件範例來做測試。

新增 directive

# ng g d hoverfocus

我們會在 src/app 下看到兩個 angular cli 產生的 directive 檔案。

  • hover-Focus.directive.ts
  • hover-Focus.directive.spec.ts

撰寫 Directive:

我們先修改 hover-Focus.directive.ts 檔案,撰寫一個『當滑鼠游標移到這個 directive 標注的 dom 上面』就把背景顏色改成黃色。

hover-Focus.directive.ts

import { Directive, ElementRef, HostListener } from '@angular/core'; @Directive({ selector: '[appHoverFocus]' }) export class HoverFocusDirective { constructor(private el: ElementRef) { } @HostListener('mouseenter') onMouseEnter() { this.highlight('yellow'); } @HostListener('mouseleave') onMouseLeave() { this.highlight(null); } private highlight(color: string) { this.el.nativeElement.style.backgroundColor = color; } }

我們可以在 login.component.html 的『登入帳號』的 dom 裡面加入這個 directive 就可以看到當滑鼠移入這個名字上,這個 h4 的 tag 背景就會被改成黃色

login.component.html
<h4 class="" appHoverFocus>登入帳號</h4>

撰寫測試:

我們先看 angular cli 幫我們產生好的 hover focus 的測試檔內容。
hover-focus.directive.spec.ts
import { HoverFocusDirective } from './hover-focus.directive'; describe('HoverFocusDirective', () => { it('should create an instance', () => { const directive = new HoverFocusDirective(); expect(directive).toBeTruthy(); }); });

在這個測試檔下是會測試失敗的!因為我們的 hover-focus 在 constructor() 裡面有加入一個 private el: ElementRef 的預設 member 所以在 const directive  = new HoverFocusDirective() 會失敗。

所以這邊我們給他一個 new directive 參數就可以使用就能通過這個測試。
hover-focus.directive.spce.ts
describe('HoverFocusDirective', () => { let el: ElementRef; it('should create an instance', () => { const directive = new HoverFocusDirective(el); expect(directive).toBeTruthy(); }); });

不過別忘了要 import ElementRef
hover-focus.directive.spec.ts
import { ElementRef } from '@angular/core';

但是上述的測試只是單純驗證這個 directive 到底有沒有存在,但是對於這個 directive 到底有沒有在滑鼠游標移在我們指定的 dom 上面時把背景改成黃色呢?因此我們必須在測試的時候產生一個有 html dom 的測試環境來針對我們的 directive 做測試。

首先我們必須產生一個有 html dom 的 template 物件,然後在這個物件上面的加入 directive 來做測試驗證
hover-focus.directive.spec.ts
@Component ({
template: `

Hello World

`
})
class TestHoverFocusComponent {}

因為我們用到 Component 的 decorator 因此要 import Compnent 進來。
hover-focus.directive.spec.ts
import { Component } from '@angular/core';

接著我們撰寫測試 login component spec 的方式在建立 TestBed 並且使用一個新的 describe()。
hover-focus.directive.spec.ts
describe('Directive: HoverFocus', () => {
let component: TestHoverFocusComponent;
let fixture: ComponentFixture<TestHoverFocusComponent>;
let titleEl: DebugElement;

beforeEach(() => {
TestBed.configureTestingModule({
declarations: [
TestHoverFocusComponent,
HoverFocusDirective]
});

fixture = TestBed.createComponent(TestHoverFocusComponent);
component = fixture.componentInstance;

// 這邊注意!改用 By 這個angular 提供的套件。
titleEl = fixture.debugElement.query(By.css('#title'));
});
});

當然要把建立 TestBed 的 module 與 library import 進來。
hover-focus.directive.spec.ts
import { By } from '@angular/platform-browser'; import { TestBed, ComponentFixture } from '@angular/core/testing'; import { DebugElement } from '@angular/core';

這邊要注意的是 titleEl 我們用的 angular 提供 By 的方式來抓取在 html 上的 title 而非之前我們使用 ElementRef 的 querySelector() 這是因為我們在後續 it() 使用 triggerEventHandler() 的 function。

再來是 it() 部份的程式碼如下:
hover-focus.directive.spec.ts
it('hovering over title.', () => { titleEl.triggerEventHandler('mouseenter', null); fixture.detectChanges(); expect(titleEl.nativeElement.style.backgroundColor).toBe('yellow'); titleEl.triggerEventHandler('mouseleave', null); fixture.detectChanges(); expect(titleEl.nativeElement.style.backgroundColor).toBe(''); });

這邊說明一下我們為什麼要使用 By.css() 來抓取我們要測試的目標。

  • 因為 triggerEventHandler() 是我們用來要求 angular 來做 mouse enter 的行為,但是這個 triggerEventHandler() 是屬於 angular 提供的功能而非 HTML Dom 本身的功能。所以如果我們使用 fixture.debugElement.nativeElement.querySelector() 來抓 title 的話。會因為 DOM 本身沒有 triggerEventHandler() 的功能變成測試產生錯誤!
另外我自己在這邊有遇到一個很奇怪的問題:
當我使用 mouseover 的時候測試會失敗,錯誤訊息是 backgroundColor 值為 '空' 值。但是在 ng test 啟用的 Chrome 瀏覽器上面的畫面自己移動滑鼠上去 title 的時候,背景是有變黃色。但是改用 mouseenter 卻有正確在測試通過。這個問題我目前還是不知道為什麼,提供給大家我遇到很奇怪的問題。

套用到 login component 上的測試

首先我們要先 import By 進來。
login.component.spec.ts
import { By } from '@angular/platform-browser';

一樣寫一個新的 describe() 與包含 HoverFocusDirective 的測試環境。
login.component.spec.ts
describe('Test directive in LoginComponent', () => { let component: LoginComponent; let fixture: ComponentFixture<LoginComponent>; let service: DemoService; let el: DebugElement; beforeEach(() => { TestBed.configureTestingModule({ declarations: [ LoginComponent, HoverFocusDirective], imports: [ ReactiveFormsModule ], providers: [ DemoService ] }); fixture = TestBed.createComponent(LoginComponent); component = fixture.componentInstance; service = TestBed.get(DemoService); el = fixture.debugElement; fixture.detectChanges(); }); });

測試 spec 上面的程式碼會跟我們做 directive unit test 很像:
login.component.spec.ts
it('test hover focus directive on LoginComponent', () => { let titleEl = el.query(By.css('h4')); titleEl.triggerEventHandler('mouseenter', null); fixture.detectChanges(); expect(titleEl.nativeElement.style.backgroundColor).toBe('yellow'); titleEl.triggerEventHandler('mouseleave', null); fixture.detectChanges(); expect(titleEl.nativeElement.style.backgroundColor).toBe(''); });

可以加上 f 讓 Jasmine 只做這個測試。
結果:

---- 待續


REF:


留言