【问题标题】:Angular unit test with @ContentChild fails使用 @ContentChild 的角度单元测试失败
【发布时间】:2019-05-14 18:45:27
【问题描述】:

我在 Angular Material 菜单组件上有一个自定义菜单关闭,我想对其进行测试,但它一直失败并输出“TypeError: Cannot read property 'closeMenu' of undefined”

规范文件(底部测试):

@Component({
  selector: 'app-test',
  template: `
    <app-header-menu>
      <button [matMenuTriggerFor]="test">Test</button>
      <mat-menu #test="matMenu">menu content</mat-menu>
    </app-header-menu>
  `
})
class TestComponent {}

describe('HeaderMenuComponent', () => {
  let component: TestComponent;
  let fixture: ComponentFixture<TestComponent>;

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [ TestComponent, HeaderMenuComponent ],
      imports: [
        MatMenuModule
      ]
    })
    .compileComponents();
  }));

  beforeEach(() => {
    fixture = TestBed.createComponent(TestComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

  it ('should close on click', () => {
    document.dispatchEvent(new MouseEvent('click'));
    expect(fixture.debugElement.query(By.css('app-header-menu')).nativeElement.trigger.closeMenu()).toHaveBeenCalled();
  });
});

组件文件:

export class HeaderMenuComponent {
  @ContentChild(MatMenuTrigger) trigger: MatMenuTrigger;

  @HostListener('document:click', ['$event'])
  onClick = event => {
    if (this.trigger && this.trigger.menuOpen && !this.elRef.nativeElement.contains(event.target)) {
      this.trigger.closeMenu();
    }
  }
}

有人知道我做错了什么吗?

【问题讨论】:

  • 我认为这里有两个问题。首先,您似乎希望在没有事先监视的情况下调用一个方法,此外您还试图访问本机 HtmlElement 上的组件实例属性。 HtmlElement 上不存在触发器。您应该使用 .componentInstance 访问您的组件,而不是 .nativeElement。但可能还有更多的东西。也许你可以在堆栈闪电战中重现这个问题,而不是我可以更有效地帮助你。
  • 这里是 stackblitz 的链接:stackblitz.com/edit/ngx-jasmine-testing-vv7cz8
  • 对不起,我有点忙。我不得不创建一个新的 stackblitz,因为你的 stackblitz 中有一些 zone.js 问题。无法异步运行测试。请在此处找到堆栈闪电战stackblitz.com/edit/angular-callback-test

标签: angular unit-testing angular-material


【解决方案1】:

如果您想测试您的@ContentChild(ren),使用包装器组件是正确的方法。

不过,要测试要关闭的触发器,您需要先将其打开,然后才能真正触发文档上的点击事件。

PFB 我的测试设置和测试本身。我还没有发现为什么单击按钮后不会打开mat-menu。我直接使用内容子触发了 openMenu。

在某种意义上,恕我直言,没有必要测试角度材料的功能,因此直接触发菜单对于您的测试用例来说应该没问题。

我对您的测试设置所做的不同之处在于,我使用了具有 MatMenuTrigger 的组件中的 componentInstance,因为本机 HtmlElement 没有触发器,也没有可监视的 closeMenu 方法。

这是stackblitz


component.spec.ts

@Component({
    selector: 'test-cmp',
    template: `<app-component>
        <button [matMenuTriggerFor]="test">Test</button>
        <mat-menu #test="matMenu">menu content</mat-menu>
    </app-component>`,
  })
  class TestWrapperComponent { }


describe('AppComponent', () => {
    let component: AppComponent;
    let fixture: ComponentFixture<TestWrapperComponent>;

    beforeEach(async(() => {
        TestBed.configureTestingModule({
            declarations: [AppComponent, TestWrapperComponent],
            providers: [],
            imports: [MatMenuModule, BrowserAnimationsModule],
            schemas: []
        })
            .compileComponents();
    }));

    beforeEach(() => {
        fixture = TestBed.createComponent(TestWrapperComponent);
        component = fixture.debugElement.children[0].componentInstance;
    });

    it('should be created', () => {
      fixture.detectChanges();
      expect(component).toBeTruthy();
    });

    it ('should close on click', fakeAsync(() => {
    fixture.detectChanges();
    const closeMenueSpy: jasmine.Spy = spyOn(component.trigger, 'closeMenu');

    // Not too sure why this menu doesn't open on click
    // const debugElem: DebugElement = fixture.debugElement.query(By.css('button'));
    // debugElem.triggerEventHandler('click', null);
    component.trigger.openMenu();
    tick();

    expect(component.trigger.menuOpen).toBeTruthy();

    document.dispatchEvent(new MouseEvent('click'));
    tick();

    expect(closeMenueSpy).toHaveBeenCalled();
  }));
});

【讨论】:

  • 菜单已打开,您只是看不到内容,因为在覆盖面板上的不透明度设置为 0。我无法解释为什么。
猜你喜欢
  • 2018-11-18
  • 1970-01-01
  • 2023-01-22
  • 2018-05-03
  • 2021-05-25
  • 2021-05-08
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多