Angular 将输出事件从子组件绑定到父组件

Angular 将输出事件从子组件绑定到父组件,angular,jasmine,Angular,Jasmine,我正在为覆盖率的组件代码中的ngAfterViewInit方法编写一个单元测试。我用茉莉花业力作为我的测试工具。目前,代码覆盖率抱怨说 不包括被称为订阅的一部分的.closeModal() 基本上,我将childcomponent引用传递给parentcomponent。如您所见,子组件具有名为closeModal的输出事件。父组件正在订阅它。正如您在下面的代码中所看到的,我在测试中做了以下操作。即将closeModal输出事件绑定到父组件,并尝试通过子组件发出事件。这似乎不太管用。我的装订方式

我正在为覆盖率的组件代码中的ngAfterViewInit方法编写一个单元测试。我用茉莉花业力作为我的测试工具。目前,代码覆盖率抱怨说 不包括被称为订阅的一部分的.closeModal()

基本上,我将childcomponent引用传递给parentcomponent。如您所见,子组件具有名为closeModal的输出事件。父组件正在订阅它。正如您在下面的代码中所看到的,我在测试中做了以下操作。即将closeModal输出事件绑定到父组件,并尝试通过子组件发出事件。这似乎不太管用。我的装订方式有问题吗

 component.ngAfterViewInit();
    component.closeModal.bind(childComponent.closeModal);
    component.componentRef.instance.closeModal.subscribe(() => {
      tick();
       childComponent.closeModal.emit(true);
      expect(component.closeModal).toHaveBeenCalled();
父组件

export class ModalDialogComponent implements AfterViewInit, OnDestroy {
  private readonly _onClose = new Subject<any>();

  public componentRef: ComponentRef<any>;
  public childComponentType: Type<any>;
  public onClose = this._onClose.asObservable();

  // add this:
  @ViewChild(InsertionDirective, { static: false })
  insertionPoint: InsertionDirective;

  constructor(public componentFactoryResolver: ComponentFactoryResolver,
              public cd: ChangeDetectorRef,
              public dialog: ModalDialogRef) {
  }

  ngAfterViewInit() {
    this.loadChildComponent(this.childComponentType);
    this.cd.detectChanges();
  }

  loadChildComponent(componentType: Type<any>) {
    const componentFactory = this.componentFactoryResolver.resolveComponentFactory(componentType);
    const viewContainerRef = this.insertionPoint.viewContainerRef;
    viewContainerRef.clear();
    this.componentRef = viewContainerRef.createComponent(componentFactory);
    this.componentRef.instance.closeModal.subscribe(() => this.closeModal());
  }

  closeModal() {
    this.dialog.close();
  }
}
导出类ModalDialogComponent实现AfterViewInit、OnDestroy{
private readonly_onClose=新主题();
公共componentRef:componentRef;
公共子组件类型:类型;
public onClose=this.\u onClose.asObservable();
//添加以下内容:
@ViewChild(InsertionDirective,{static:false})
插入点:InsertionDirective;
构造函数(公共componentFactoryResolver:componentFactoryResolver,
公共cd:ChangeDetectorRef,
公共对话框:ModalDialogRef){
}
ngAfterViewInit(){
this.loadChildComponent(this.childComponentType);
this.cd.detectChanges();
}
loadChildComponent(componentType:Type){
const componentFactory=this.componentFactoryResolver.resolveComponentFactory(componentType);
const viewContainerRef=this.insertionPoint.viewContainerRef;
viewContainerRef.clear();
this.componentRef=viewContainerRef.createComponent(componentFactory);
this.componentRef.instance.closeModal.subscribe(()=>this.closeModal());
}
closeModal(){
this.dialog.close();
}
}
子组件

@Component({
  selector: 'app-example',
  templateUrl: './example.component.html'
})

export class ExampleComponent {
  @Output() closeModal = new EventEmitter<boolean>();
  constructor(public config: ModalDialogConfig,
              public dialog: ModalDialogRef
              ) {}

  onCancel() {
    this.dialog.close('user clicked cancel');
  }

  onOk() {
    this.dialog.close('User clicked ok');
  }
}
@组件({
选择器:“应用程序示例”,
templateUrl:“./example.component.html”
})
导出类ExampleComponent{
@Output()closeModal=neweventemitter();
构造函数(公共配置:ModalDialogConfig,
公共对话框:ModalDialogRef
) {}
onCancel(){
此.dialog.close('用户单击取消');
}
onOk(){
this.dialog.close('User clicked ok');
}
}
试验

fdescribe('ModalDialogComponent',()=>{
let组件:ModalDialogComponent;
let-childComponent:ExampleComponent;
let夹具:组件夹具;
let childFixture:组件fixture;
让mockMouseEvent;
beforeach(异步(()=>{
TestBed.configureTestingModule({
导入:[SharedModule、ModalDialogModule、NgxsModule.forRoot([])],
提供者:[ModalDialogConfig,ModalDialogRef]
})
.overrideModule(BrowserDynamicTestingModule,{set:{entryComponents:[ModalDialogComponent,ExampleComponent]})
.compileComponents();
}));
在每个之前(()=>{
fixture=TestBed.createComponent(ModalDialogComponent);
childFixture=TestBed.createComponent(例如Component);
mockMouseEvent=newmock({stopPropagation:()=>Promise.resolve(true)});
组件=fixture.componentInstance;
childComponent=childFixture.componentInstance;
component.childComponentType=示例组件;
component.componentRef=childFixture.componentRef;
spyOn(component.componentRef.instance,'closeModal')和.returnValue(Observable.of(true));
fixture.detectChanges();
});
它('应该调用ngAfterViewInit',()=>{
ngAfterViewInit();
component.closeModal.bind(childComponent.closeModal);
component.componentRef.instance.closeModal.subscribe(()=>{
勾选();
childComponent.closeModal.emit(true);
expect(component.closeModal).tohavebeencall();
});
});

问题已经解决。我只需要添加这行代码
component.componentRef.instance.closeModal.emit(true);

注意
spyOn(component.dialog,'close')。和.callThrough();
将不会调用(执行)方法,但将确保spy在调用时将使用原始函数而不是存根函数。那么为什么不涵盖我要涵盖的方法呢?您确定
this.componentRef.instance.closeModal
发出吗?我认为它发出的原因是,一旦我得到关于它发出的错误,subscribe基本上正在寻找r未找到的事件
fdescribe('ModalDialogComponent', () => {
  let component: ModalDialogComponent;
  let childComponent: ExampleComponent;
  let fixture: ComponentFixture<ModalDialogComponent>;
  let childFixture: ComponentFixture<ExampleComponent>;
  let mockMouseEvent;

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      imports: [SharedModule, ModalDialogModule, NgxsModule.forRoot([])],
      providers: [ModalDialogConfig, ModalDialogRef ]
    })
    .overrideModule(BrowserDynamicTestingModule, { set: { entryComponents: [ModalDialogComponent, ExampleComponent] } })
    .compileComponents();
  }));

  beforeEach(() => {
    fixture = TestBed.createComponent(ModalDialogComponent);
    childFixture = TestBed.createComponent(ExampleComponent);
    mockMouseEvent = new Mock<MouseEvent>({ stopPropagation: () => Promise.resolve(true) });
    component = fixture.componentInstance;
    childComponent = childFixture.componentInstance;

    component.childComponentType = ExampleComponent;

    component.componentRef = childFixture.componentRef;
    spyOn(component.componentRef.instance, 'closeModal').and.returnValue(Observable.of(true));
    fixture.detectChanges();
  });

  it('should call ngAfterViewInit ', () => {

    component.ngAfterViewInit();
    component.closeModal.bind(childComponent.closeModal);
    component.componentRef.instance.closeModal.subscribe(() => {
      tick();
        childComponent.closeModal.emit(true);
      expect(component.closeModal).toHaveBeenCalled();

    });

  });