0

我想测试一个 Angular 组件并将 contentChild 替换为 Mock。我遵循了本指南:https ://medium.com/angular-in-depth/angular-unit-testing-viewchild-4525e0c7b756 该指南适用于 viewChild,但我认为这也适用于内容儿童。

我的 contentChild 有一个父订阅的 Observable。如果我用真正的孩子测试代码,它就可以工作。如果我嘲笑孩子,测试就行不通。我认为我在测试中查询以发出新值的子模拟是一个不同的实例,而不是父订阅的那个。

我的父母:

@Component({
  // tslint:disable-next-line:component-selector
  selector: '[appParent]',
  template: `
    <div>
      <span class="true" *ngIf="display$ | async; else other">True</span>
    </div>
    <ng-content></ng-content>
    <ng-template #other><span class="false">False</span></ng-template>
  `
})
export class ParentComponent implements AfterContentInit {

  @ContentChild(ChildComponent) child!: ChildComponent;

  display$: Observable<boolean>;

  ngAfterContentInit(): void {
    this.display$ = this.child.display$;
  }
}

我的模拟:

@Component({
  selector: 'app-child',
  template: '',
  providers: [
    {
      provide: ChildComponent,
      useExisting: ChildStubComponent
    }
  ]
})
export class ChildStubComponent {
  displaySubject = new BehaviorSubject(false);
  display$: Observable<boolean> = this.displaySubject.asObservable();
}

和测试:

describe('ParentComponentTest', () => {
  @Component({
    template: `
      <div appParent>
        <app-child></app-child>
      </div>
    `
  })
  class TestComponent {
    @ViewChild(ChildStubComponent)
    child!: ChildStubComponent;
  }

  let component: TestComponent;
  let fixture: ComponentFixture<TestComponent>;

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

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

  it('should find true', () => {
    component.child.displaySubject.next(true);
    fixture.detectChanges();
    expect(fixture.debugElement.query(By.css('.true'))).toBeTruthy();
  });

});
4

2 回答 2

0

您可以使用具有模拟功能的测试库,例如ng-mocks

它提供MockBuilderMockComponent

它还MockInstance允许您在初始化之前模拟组件方法/属性。

我认为您的情况类似于如何测试声明的 ViewChild / ViewChildren

您的测试可能如下所示:

describe('ParentComponentTest', () => {
  // keeping ParentComponent as it is and mocking ChildStubComponent
  beforeEach(() => MockBuilder(ParentComponent).mock(ChildStubComponent));

  it('should find true', () => {
    // providing a mock upfront
    const displaySubject = MockInstance(
      ChildStubComponent,
      'displaySubject',
      new Subject(),
    );

    // rendering desired template
    const fixture = MockRender(`
      <div appParent>
        <app-child></app-child>
      </div>
    `);

    // testing behavior
    displaySubject.next(true);
    fixture.detectChanges();
    expect(ngMocks.find('.true')).toBeTruthy();
  });
});
于 2021-03-22T08:57:38.017 回答
0

问题接缝是,你得到的 view-child 不是正确的实例。解决方案是从父组件的子属性中获取实例。


describe('ParentComponentTest', () => {
  @Component({
    template: `
      <div appParent>
        <app-child></app-child>
      </div>
    `
  })
  class TestComponent {
    @ViewChild(ParentComponent)
    parent!: ParentComponent;
  }

  let component: TestComponent;
  let fixture: ComponentFixture<TestComponent>;

  ....

  it('should find true', () => {
    const childStub = component.parent.child.toArray()[0] as unknown as ChildStubComponent;
    childStub.displaySubject.next(true);
    fixture.detectChanges();
    expect(fixture.debugElement.query(By.css('.true'))).toBeTruthy();
  });

});


于 2021-03-31T14:02:33.493 回答