【问题标题】:How test directive with Renderer2?如何用 Renderer2 测试指令?
【发布时间】:2019-06-24 13:45:28
【问题描述】:

我创建了一个小指令来防止默认事件传递给它。

@Directive({
    selector: '[sPreventDefault]'
})
export class PreventDefaultDirective {
    private events: (() => void)[] = [];
    @Input('sPreventDefault') set listenOn(events: string | string[]) {
        this.removeListeners();

        if (typeof events == 'string') {
            events = [events];
        }
        this.registerEventListener(
            events,
            e => {
                if (e instanceof Event) {
                    e.stopPropagation();
                } else {
                    e.srcEvent.stopPropagation();
                }
            },
        );
    }

    constructor(private elementRef: ElementRef<HTMLElement>, private renderer: Renderer2) {
        super(elementRef, renderer);
    }

    protected registerEventListener(listenOn: string[], eventListener: (e: Event | HammerJSEvent) => void): void {
        this.events = listenOn.map(eventName => {
            return this.renderer.listen(this.elementRef.nativeElement, eventName, eventListener);
        });
    }
    protected removeListeners(): void {
        this.events.forEach(dispose => dispose());
        this.events = [];
    }
}

测试服

@Component({
    selector: 'test-host',
    template: `<div [sPreventDefault]="events">`,
})
class TestHostComponent {
    @ViewChild(PreventDefaultDirective) directive!: PreventDefaultDirective;
    @Input() events: PreventDefaultDirective['listenOn'] = [];
}

fdescribe('PreventDefaultDirective', () => {
    let host: TestHostComponent;
    let hostElement: DebugElement;
    let fixture: ComponentFixture<TestHostComponent>;
    let directive: PreventDefaultDirective;

    beforeEach(async(() => {
        TestBed.configureTestingModule({
            declarations: [
                TestHostComponent,
                PreventDefaultDirective,
            ],
        }).compileComponents();

        fixture = TestBed.createComponent(TestHostComponent);
        hostElement = fixture.debugElement;
        host = fixture.componentInstance;
        directive = host.directive;
    }));

    it('should create an instance', () => {
        host.events = ['testEvent'];
        fixture.detectChanges();
        expect(directive).toBeTruthy();
    });

    it('should add listener', () => {
        host.events = ['testEvent'];
        fixture.detectChanges();

        //  DebugElement.listeners is null
        expect(hostElement.listeners.length).toBe(1);
        expect(hostElement.listeners.map(l => l.name)).toBe(host.events);
    });
});

我的问题是,DebugElement,似乎不知道通过Renderer2.listen 方法注册的事件。测试这个的正确方法是什么?

【问题讨论】:

    标签: angular unit-testing testing


    【解决方案1】:

    好吧,既然感兴趣,我检查了一些东西,但它不会是直接的答案,甚至不会解决你的问题,尽管你可以找到一些有用的东西。

    看起来hostElement.listeners 指向由@HostListener 装饰器添加的事件侦听器,您可以轻松检查。 我也尝试找到检索听众的方法,但没有成功。

    我在这里要做的是检查何时调用 renderer.listen 并将实现抛在后面 - 这种方式对于高于“单元测试”的测试可能是错误的,因为您想检查这是否真的有效但对于单元它应该是美好的。如果你想监视Renderer2,你可以这样做:

    const renderer = fixture.componentRef.injector.get(Renderer2);
    const listenSpy = spyOn(renderer, 'listen');
    expect(listenSpy).toHaveBeenCalled();
    

    编辑 1: 由于提供的获取Renderer2 的方法已被弃用,因此应该使用:

    const renderer = fixture.componentRef.injector.get(Renderer2 as Type<Renderer2>);
    

    【讨论】:

    • 这确实有效,我完全忘记了喷油器!我只想问一下弃用,这对我来说似乎是误报,但永远不确定。错误:get is deprecated: from v4.0.0 use Type&lt;T&gt; or InjectionToken&lt;T&gt; (deprecation)
    • 关于其余的,你是完全正确的,但我想测试它是否被调用、清理以及它是否真的阻止了默认。所有这些都可以通过间谍来完成。
    • @Akxe 你是对的,有了这个它不应该再抱怨了:const renderer = fixture.componentRef.injector.get&lt;Renderer2&gt;(Renderer2 as Type&lt;Renderer2&gt;);
    • 是的,但你不需要&lt;Renderer2&gt;,因为get 的类型定义从Type&lt;T&gt; 的泛型推断类型。然而我不知道为什么这是必要的?它是 Angular 组件!
    猜你喜欢
    • 2023-03-02
    • 2021-10-06
    • 2014-01-13
    • 2014-07-02
    • 2019-10-07
    • 2015-02-16
    • 2019-05-31
    • 2019-12-24
    • 1970-01-01
    相关资源
    最近更新 更多