测试 Angular 个指令
Testing Angular Directives
我有一个附加到元素事件的 angular 指令:
@Directive({
selector: '[myDirective]'
})
export class MyDirective {
@HostListener('click', ['$event']) click(event: Event): void {
debugger;
console.log(event); // <-- this is null in unit tests, MouseEvent when running the app normally
}
}
这工作正常,但由于某些原因,在对指令进行单元测试时事件参数是 null
。
我的 Karma Jasmine 单元测试设置:
import { CommonModule } from '@angular/common';
import { Component, DebugElement, ElementRef, Injector } from '@angular/core';
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
@Component({
selector: 'host-component',
template: `
<input type="text" myDirective id="findMe"/>
`
})
class HostComponent {
}
describe(`MyDirective`, () => {
let host: HostComponent;
let fixture: ComponentFixture<HostComponent>;
let debugElement: DebugElement;
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [
CommonModule
],
declarations: [
HostComponent, MyDirective
]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(HostComponent);
host = fixture.componentInstance;
debugElement = fixture.debugElement.query(By.css('#findMe'))
fixture.autoDetectChanges();
});
describe(`should listen to the events`, () => {
it(`should listen to the click event`, () => {
fixture..triggerEventHandler('click', null);
fixture.detectChanges();
// expect...
});
});
});
现在,问题是:指令在单元测试中被命中,但没有 event
作为参数发送。
I've followed this example: https://codecraft.tv/courses/angular/unit-testing/directives/ but unfortunately it doesnt use the event parameter.
编辑
我还按照 this example 将参数传递给 @HostListener()
装饰器:
@HostListener('mouseenter', ['$event.target.id'])
onMouseEnter(id: string) {
// Logs the id of the element
// where the event is originally invoked.
console.log(id);
}
编辑 2
似乎从 DebugElement 引发的事件并不真正代表来自 DOM 元素的实际事件侦听器?
根据 Hojou 所说 on this angular github issue,如果您从 nativeElement 触发事件,它就会起作用。所以下面的代码确实通过事件发送到指令,只是不太确定它是否是正确的方式:
describe(`should listen to the events`, () => {
it(`should listen to the click event`, () => {
// fixture.triggerEventHandler('click', null);
debugElement.nativeElement.dispatchEvent(newEvent('click'));
fixture.detectChanges();
// expect...
});
});
function newEvent(eventName: string) {
const customEvent: CustomEvent<any> = document.createEvent('CustomEvent'); // MUST be 'CustomEvent'
customEvent.initCustomEvent(eventName, false, false, null);
return customEvent;
}
你在那里得到 null 因为你在
中将 null
作为参数传递
fixture..triggerEventHandler('click', null);
我认为是错字,应该是
debugElement.triggerEventHandler('click', null);
如果你在那里传递一个对象,你会看到它被记录在指令中
debugElement.triggerEventHandler('click', {test: 'test'});
我想补充一点,我个人会通过 'executing' 单击实际的 DOM 对象来进行此测试,因此您不需要自己指定/存根事件,这似乎做一个更值得信赖的测试。
所以你可以代替 triggerEventHandler
行来做类似
的事情
debugElement.nativeElement.click()
From the Edit 2, raising a custom event does the trick and sends through the attached element of the directive
根据 Hojou 在 this angular github issue (#22148, currently 'Open') 上所说的,如果您从 nativeElement 触发事件,它就会起作用。所以下面的代码确实通过事件发送到指令,只是不太确定它是否正确:
describe(`should listen to the events`, () => {
it(`should listen to the click event`, () => {
// fixture.triggerEventHandler('click', null);
debugElement.nativeElement.dispatchEvent(newEvent('click'));
fixture.detectChanges();
// expect...
});
});
function newEvent(eventName: string) {
const customEvent: CustomEvent<any> = document.createEvent('CustomEvent'); // MUST be 'CustomEvent'
customEvent.initCustomEvent(eventName, false, false, null);
return customEvent;
}
您不直接测试指令。您通过测试组件测试它们。我在这里测试这里描述的 *ngVar 指令 -
这里的主要内容是测试指令时使用与测试组件时完全相同的方法。并根据测试组件执行它应该执行的操作来测试指令的行为!
import { NgVarDirective } from './ng-var.directive';
import { Component, DebugElement } from '@angular/core';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { DirectivesModule } from '../directives.module';
import { By } from '@angular/platform-browser';
@Component({
template: '<ng-container *appNgVar="42 as myVar"><div>{{ myVar }}</div></ng-container>'
})
class TestComponent { }
describe('NgVarDirective', () => {
let component: TestComponent;
let fixture: ComponentFixture<TestComponent>;
beforeEach(() => {
TestBed.configureTestingModule({
declarations: [TestComponent],
imports: [DirectivesModule]
});
fixture = TestBed.createComponent(TestComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
it('should create an instance (HTMLElement)', () => {
const el: HTMLElement = fixture.debugElement.nativeElement;
const div: HTMLDivElement = el.querySelector('div');
expect(div.textContent).toBe('42');
});
it('should create an instance (DebugElement)', () => {
const el: DebugElement = fixture.debugElement;
const de: DebugElement = el.query(By.css('div'));
expect(de.nativeElement.textContent).toBe('42');
});
});
这里是另一个例子(它没有测试结构指令,不像我上面的例子)https://codecraft.tv/courses/angular/unit-testing/directives
我有一个附加到元素事件的 angular 指令:
@Directive({
selector: '[myDirective]'
})
export class MyDirective {
@HostListener('click', ['$event']) click(event: Event): void {
debugger;
console.log(event); // <-- this is null in unit tests, MouseEvent when running the app normally
}
}
这工作正常,但由于某些原因,在对指令进行单元测试时事件参数是 null
。
我的 Karma Jasmine 单元测试设置:
import { CommonModule } from '@angular/common';
import { Component, DebugElement, ElementRef, Injector } from '@angular/core';
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
@Component({
selector: 'host-component',
template: `
<input type="text" myDirective id="findMe"/>
`
})
class HostComponent {
}
describe(`MyDirective`, () => {
let host: HostComponent;
let fixture: ComponentFixture<HostComponent>;
let debugElement: DebugElement;
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [
CommonModule
],
declarations: [
HostComponent, MyDirective
]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(HostComponent);
host = fixture.componentInstance;
debugElement = fixture.debugElement.query(By.css('#findMe'))
fixture.autoDetectChanges();
});
describe(`should listen to the events`, () => {
it(`should listen to the click event`, () => {
fixture..triggerEventHandler('click', null);
fixture.detectChanges();
// expect...
});
});
});
现在,问题是:指令在单元测试中被命中,但没有 event
作为参数发送。
I've followed this example: https://codecraft.tv/courses/angular/unit-testing/directives/ but unfortunately it doesnt use the event parameter.
编辑
我还按照 this example 将参数传递给 @HostListener()
装饰器:
@HostListener('mouseenter', ['$event.target.id'])
onMouseEnter(id: string) {
// Logs the id of the element
// where the event is originally invoked.
console.log(id);
}
编辑 2
似乎从 DebugElement 引发的事件并不真正代表来自 DOM 元素的实际事件侦听器?
根据 Hojou 所说 on this angular github issue,如果您从 nativeElement 触发事件,它就会起作用。所以下面的代码确实通过事件发送到指令,只是不太确定它是否是正确的方式:
describe(`should listen to the events`, () => {
it(`should listen to the click event`, () => {
// fixture.triggerEventHandler('click', null);
debugElement.nativeElement.dispatchEvent(newEvent('click'));
fixture.detectChanges();
// expect...
});
});
function newEvent(eventName: string) {
const customEvent: CustomEvent<any> = document.createEvent('CustomEvent'); // MUST be 'CustomEvent'
customEvent.initCustomEvent(eventName, false, false, null);
return customEvent;
}
你在那里得到 null 因为你在
中将null
作为参数传递
fixture..triggerEventHandler('click', null);
我认为是错字,应该是
debugElement.triggerEventHandler('click', null);
如果你在那里传递一个对象,你会看到它被记录在指令中
debugElement.triggerEventHandler('click', {test: 'test'});
我想补充一点,我个人会通过 'executing' 单击实际的 DOM 对象来进行此测试,因此您不需要自己指定/存根事件,这似乎做一个更值得信赖的测试。
所以你可以代替 triggerEventHandler
行来做类似
debugElement.nativeElement.click()
From the Edit 2, raising a custom event does the trick and sends through the attached element of the directive
根据 Hojou 在 this angular github issue (#22148, currently 'Open') 上所说的,如果您从 nativeElement 触发事件,它就会起作用。所以下面的代码确实通过事件发送到指令,只是不太确定它是否正确:
describe(`should listen to the events`, () => {
it(`should listen to the click event`, () => {
// fixture.triggerEventHandler('click', null);
debugElement.nativeElement.dispatchEvent(newEvent('click'));
fixture.detectChanges();
// expect...
});
});
function newEvent(eventName: string) {
const customEvent: CustomEvent<any> = document.createEvent('CustomEvent'); // MUST be 'CustomEvent'
customEvent.initCustomEvent(eventName, false, false, null);
return customEvent;
}
您不直接测试指令。您通过测试组件测试它们。我在这里测试这里描述的 *ngVar 指令 -
这里的主要内容是测试指令时使用与测试组件时完全相同的方法。并根据测试组件执行它应该执行的操作来测试指令的行为!
import { NgVarDirective } from './ng-var.directive';
import { Component, DebugElement } from '@angular/core';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { DirectivesModule } from '../directives.module';
import { By } from '@angular/platform-browser';
@Component({
template: '<ng-container *appNgVar="42 as myVar"><div>{{ myVar }}</div></ng-container>'
})
class TestComponent { }
describe('NgVarDirective', () => {
let component: TestComponent;
let fixture: ComponentFixture<TestComponent>;
beforeEach(() => {
TestBed.configureTestingModule({
declarations: [TestComponent],
imports: [DirectivesModule]
});
fixture = TestBed.createComponent(TestComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
it('should create an instance (HTMLElement)', () => {
const el: HTMLElement = fixture.debugElement.nativeElement;
const div: HTMLDivElement = el.querySelector('div');
expect(div.textContent).toBe('42');
});
it('should create an instance (DebugElement)', () => {
const el: DebugElement = fixture.debugElement;
const de: DebugElement = el.query(By.css('div'));
expect(de.nativeElement.textContent).toBe('42');
});
});
这里是另一个例子(它没有测试结构指令,不像我上面的例子)https://codecraft.tv/courses/angular/unit-testing/directives