【问题标题】:Testing Angular Directives测试 Angular 指令
【发布时间】:2019-12-24 17:50:05
【问题描述】:

我有一个附加到元素上的事件的角度指令:

@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 触发事件,它就会起作用。所以下面的代码确实通过事件发送到指令,只是不太确定它是否是正确的方式:

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;
}

【问题讨论】:

    标签: angular unit-testing karma-jasmine angular-directive


    【解决方案1】:

    因为您将null 作为参数传递给

    fixture..triggerEventHandler('click', null);
    

    我认为这是一个错字,应该是

    debugElement.triggerEventHandler('click', null);
    

    如果你在那里传递一个对象,你会看到它被记录在指令中

    debugElement.triggerEventHandler('click', {test: 'test'});
    

    我要补充一点,我个人会通过“执行”实际 DOM 对象的单击来进行此测试,因此您不需要自己指定/存根事件,这似乎是一个更值得信赖的测试。

    所以你可以代替triggerEventHandler 行,你会做类似的事情

    debugElement.nativeElement.click()
    

    【讨论】:

    • 你试过了吗?它通过 null 发送无论您通过那里,否则我可能会遗漏一些东西。无论哪种方式,该指令都应该发送预期的内容,如@HostListener() 中定义的那样,这是绑定'$event'
    • 我仍在使用element.dispatchEvent 通过null 发送邮件,但那个正在工作...
    【解决方案2】:

    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;
    }
    

    【讨论】:

      【解决方案3】:

      您不直接测试指令。您可以通过测试组件对其进行测试。在这里,我正在测试这里描述的 *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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-10-18
        • 2019-11-11
        • 1970-01-01
        • 2012-11-21
        • 2015-01-18
        • 1970-01-01
        • 2019-08-12
        • 2018-09-30
        相关资源
        最近更新 更多