我有一个角度指令,该指令附加到元素上的事件:
@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
作为参数发送。
我遵循了以下示例:https://codecraft.tv/courses/angular/unit-testing/directives/,但不幸的是它没有使用事件参数。
修改
我还遵循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触发事件,它将起作用。因此,以下代码确实将事件发送至指令,只是不太确定其 right 方式是否正确:
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;
}
答案 0 :(得分:0)
您在那里得到的是空值,因为您将
中的null
作为参数传递
fixture..triggerEventHandler('click', null);
我认为是错字,应该是
debugElement.triggerEventHandler('click', null);
如果您在此处传递对象,则会看到该对象已记录在指令中
debugElement.triggerEventHandler('click', {test: 'test'});
我个人认为我会通过“执行”实际DOM对象的单击来进行此测试,因此您无需自己指定/添加事件,这似乎使测试更加值得信赖。
所以您将代替triggerEventHandler
行
debugElement.nativeElement.click()
答案 1 :(得分:0)
在 Edit 2 中,引发自定义事件可以解决问题,并通过指令的附加元素进行发送
根据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;
}
答案 2 :(得分:0)
您不直接测试指令。您可以通过测试组件测试它们。在这里,我正在测试此处描述的 *ngVar 指令 - How to declare a variable in a template in Angular
这里的主要内容是测试指令时使用与测试组件时完全相同的方法。并根据测试组件执行它应该做的事情来测试指令的行为!
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