25

我正在使用角度 5.2.0。我有一个子组件

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

@Component({
  template: `<div><div></div></div>`,
})
export class ChildComponent {

  public childMethod() {
    ...
  }
}

以及通过以下方式访问子组件的父组件ViewChild

import { Component, ViewChild } from "@angular/core";
import { ChildComponent } from "child.component";

@Component({
  template: `
  <child-component #child>
    <child-component></child-component>
  </child-component>
  `,
})
export class ParentComponent {
  @ViewChild("child") child: ChildComponent;

  public parentMethod() {
    this.child.childMethod();
  }
}

我想要一个单元测试来证明调用parentMethod会导致调用childMethod. 我有以下内容:


import { NO_ERRORS_SCHEMA } from "@angular/core";
import { ComponentFixture, TestBed } from "@angular/core/testing";
import { ChildComponent } from "./child.component";
import { ParentComponent } from "./parent.component";

describe("ParentComponent", () => {
  let component: Parentcomponent;
  let fixture: ComponentFixture<Parentcomponent>;

  beforeEach(() => {
    TestBed.configureTestingModule({
      declarations: [ParentComponent, ChildComponent],
      schemas: [NO_ERRORS_SCHEMA],
    }).compileComponents();
  });

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

  it("should invoke childMethod when parentMethod is invoked", () => {
    const childMethodSpy: jasmine.Spy = spyOn(component.child, "childMethod");
    component.parentMethod();
    expect(childMethodSpy).toHaveBeenCalled();
  });
});

然而,这不起作用,我得到Error: <spyOn> : could not find an object to spy upon for childMethod().

此外,这不是单元测试,因为我使用的是真正的 ChildComponent 而不是模拟。我尝试创建一个 MockChildComponent 并将其添加到declarationsexport但我得到了相同的结果。有什么帮助吗?

我知道有类似的帖子,但它们适用于不同版本的角度,它们没有帮助。

4

3 回答 3

46

你可以做这样的事情。

像这样创建一个间谍对象ChildComponent

const childComponent = jasmine.createSpyObj('ChildComponent', ['childMethod']);

然后在测试中,将组件的 childComponent 属性设置为您创建的 spy。

component.childComponent =  childComponent;

您的测试文件应如下所示。

import { NO_ERRORS_SCHEMA } from "@angular/core";
import { ComponentFixture, TestBed } from "@angular/core/testing";
import { ChildComponent } from "./child.component";
import { ParentComponent } from "./parent.component";

describe("ParentComponent", () => {
  let component: ParentComponent;
  let fixture: ComponentFixture<ParentComponent>;

  const childComponent = jasmine.createSpyObj("ChildComponent", [
    "childMethod",
  ]);

  beforeEach(() => {
    TestBed.configureTestingModule({
      declarations: [ParentComponent, ChildComponent],
      schemas: [NO_ERRORS_SCHEMA],
    }).compileComponents();
  });

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

  it("should invoke childMethod when parentMethod is invoked", () => {
    component.childComponent = childComponent;
    component.parentMethod();
    expect(childComponent.childMethod).toHaveBeenCalled();
  });
});


于 2018-07-25T09:49:30.027 回答
1

您可以模拟 ChildComponent,因此您可以单独测试 ParentComponent,而无需手动分配 ChildComponent。

如果 ParentComponent.childComponent 是私有的,则这是必需的,如果 ChildComponent 有许多依赖项,则几乎是必需的。

您的测试文件应如下所示:

import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ChildComponent } from './child.component';
import { ParentComponent } from './parent.component';

@Component({
  selector: 'child-component',
  template: '',
  providers: [{ provide: ChildComponent, useClass: ChildStubComponent }]
})
class ChildStubComponent {
  childMethod = jasmine.createSpy('childMethod');
}

describe('ParentComponent', () => {S
  let component: ParentComponent;
  let fixture: ComponentFixture<ParentComponent>;

  beforeEach(() => {
    TestBed.configureTestingModule({
      declarations: [ ParentComponent, ChildStubComponent ]
    }).compileComponents();
  });

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

  it('should invoke childMethod when parentMethod is invoked', () => {
    component.childComponent =  childComponent;
    component.parentMethod();
    expect(childComponent.childMethod).toHaveBeenCalled();
  });
});

学分:来自Angular inDepth的想法。基于@Anuradha Gunasekara 的回答。

于 2021-09-02T15:57:22.777 回答
0

或者,覆盖此类代码的快速简便的方法....

如果 .ts 文件是这样的:

@ViewChild('datePicker') myDatePicker: AngularMyDatePickerDirective

然后在 it() 块内的 spec.ts 文件中,您可以添加如下内容:

component.myDatePicker = { toggleCalender: () => Promise.resolve()} as unknown as AngularMyDatePickerDirective;
于 2022-02-03T06:01:08.087 回答