Angular 2+:使用mat-slide-toggle测试表单-更改事件不会触发

28

我不确定为什么更改事件没有触发,因此也没有触发我为更改事件注册的函数。

我知道当我运行它时它可以工作,但我无法在规范中得到它。

这是我所说的示例演示... 如您从日志中看到的那样,切换永远不会被调用:https://stackblitz.com/edit/angular-material-slide-toggle-test-utmbmy?file=app%2Fhello.component.spec.ts

大致上,这是我的代码...

component.html:

<mat-slide-toggle [checked]="useDefault" (change)="toggle($event)"></mat-slide-toggle>

组件.ts:

...
toggle(event: MatSlideToggleChange) {
  console.log('Toggle fired');
  this.useDefault = event.checked;
}

在我的 spec.ts 文件中:

it('should trigger toggle...', () => {
  const componentDebug = fixture.debugElement;
  const slider: MatSlideToggle = 
     componentDebug.query(By.css('form mat-slide-toggle')).componentInstance;

  console.log('before ' + slider.checked);
  slider.toggle();

  fixture.detectChanges();

  console.log('after ' + slider.checked);
  console.log('useDefault ' + component.useDefault);
});

after 日志确实打印出了 slider.checked 的值与 before 相反。但是,component.useDefault 的值保持不变,并且在 toggle 函数中的日志语句从未被调用,这意味着它从未被触发。

为什么会出现这种情况,我该如何解决?

我试过将整个内容放入 fakeAsync,使用 tick,并将“after”代码放入 fixture.whenStable()fixture.whenRenderingDone() 中。

TestBedimport 中,我有一个 NoopAnimationsModule


useDefault是什么?它来自于mat-slide-toggle吗?还是你创建的指令? - Leo Caseiro
https://stackblitz.com/edit/angular-material-slide-toggle-test?file=app/hello.component.ts - Leo Caseiro
好的,我更新了我的问题代码。useDefault只是一个拼写错误,应该是[checked] =“useDefault”。但是,我稍微修改了您的组件,使其更符合我的要求,即从useDefault = true开始。我只添加了一些日志记录语句。现在,由于我无法理解的原因,toggle日志不再显示:https://stackblitz.com/edit/angular-material-slide-toggle-test-utmbmy?file=app%2Fhello.component.spec.ts - user1902183
1
尝试直接触发更改事件:componentDebug.query(By.css('mat-slide-toggle')).nativeElement.dispatchEvent(new Event('change')); 你可能还需要以某种方式模拟MatSlideToggleChange对象。 - G. Tranter
4个回答

29

使用第三方组件时,应该信任它们来处理事件。但是,您可以使用triggereventhandler触发事件,同时spyOn您想要调用的方法。

组件

import { Component } from '@angular/core';
import { MatSlideToggleChange } from '@angular/material';

@Component({
    selector: 'hello',
    template: `
        <mat-slide-toggle
            [checked]="useDefault" (change)="toggle($event)"
        ></mat-slide-toggle>`
})
export class HelloComponent  {
    public useDefault = false;

    public toggle(event: MatSlideToggleChange) {
        console.log('toggle', event.checked);
        this.useDefault = event.checked;
    }
}

测试

it('should call change method on slide change', () => {
    const componentDebug = fixture.debugElement;
    const slider = componentDebug.query(By.directive(MatSlideToggle));
    spyOn(component, 'toggle'); // set your spy

    slider.triggerEventHandler('change', null); // triggerEventHandler

    expect(component.toggle).toHaveBeenCalled(); // event has been called
});

在 StackBlitz 上查看测试通过情况:https://stackblitz.com/edit/angular-material-slide-toggle-test-spy-trigger-event?file=app/hello.component.spec.ts


1
我不确定你在相信第三方组件处理事件的观点上是正确的。我可以相信它们会发出事件,但我需要测试我的模板绑定是否正确。因此,直接调用我的绑定函数肯定会测试到该函数。然而,假设稍后有人来编辑我的组件模板并删除了 (change)="onToggle()"。测试仍然会通过,因为我直接测试 onToggle,但应用程序将无法正常工作,因为事件绑定现在已经丢失了。我很快就会发布我的解决方案…… - user1902183
1
简而言之,目前的解决方案是执行 const slider: HtmlElement = componentDebug.query(By.css('form mat-slide-toggle input')).nativeElement; 然后执行 slider.click()。这实际上会使 MatSlideToggle 发出 change 事件。我认为这是 MatSlideToggle 组件中的一个 bug。它的 toggle() 函数翻转了 this.checked = !this.checked,但没有发出 change 事件。我认为这是一个 bug。应该发出 change 事件。 - user1902183
@user1902183,实际上如果你删除(change)="onToggle()",你的测试将会失败(请参见 https://stackblitz.com/edit/angular-material-slide-toggle-test-spy-trigger-event-not-called)。在我看来,你应该为切换内部逻辑编写一个单元测试,并为更改事件调用另一个测试(就像例子中一样)。 - Leo Caseiro
这里是相同的测试,但现在使用 mat-slide-toggle input 作为选择器。一切都正常工作,尽管我认为切换应该发出更改事件,而不需要这种“hack”。https://stackblitz.com/edit/angular-material-slide-toggle-test-2vbj7p?file=app/hello.component.spec.ts - user1902183

17

你的组件.html

    <mat-slide-toggle (change)="onToggle($event)">Compare</mat-slide-toggle>

在你的组件中你可以检查

    event.checked = true || false;

0

通过 css 选择器 'form mat-slide-toggle input',就像 user1902183 提到的那样,解决了问题。


0
有点晚来参加派对,但也许这会对其他开发人员有所帮助,他们因为测试使用 Angular Material 组件的模板时事件没有触发而来到这里。 由于类似问题,已经有多个问题被提出,但在其中没有一个问题中我找到了关于使用组件测试工具的建议,这个工具是在 Angular 9 中引入的。
这是一种更简单、更简洁、更易读的方式来对 Angular Material 元素进行测试。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接