【问题标题】:Angular 5 testing: how to get a reference to the child componentAngular 5 测试:如何获取对子组件的引用
【发布时间】:2021-11-18 02:05:20
【问题描述】:

我正在尝试测试 Angular 应用程序中主机组件和子组件之间的交互。我不知道如何获取对创建父组件时创建的子组件的引用。这是设置:

child.component.spec.ts

@Component({template: `<child [data]="model"></child>`})
class HostComponent {
  public model:any;
}

describe('ChildComponent', () => {
  let hostFixture: ComponentFixture<HostComponent>;
  let childFixture: ComponentFixture<ChildComponent>;

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [ChildComponent, HostComponent]
    });
  }));

  beforeEach(() => {
    // this creates the child component as well, but how to get to it?
    hostFixture = TestBed.createComponent(HostComponent);

    // this creates a NEW instance of child component, not the one from the Host template
    // so it's not the instance I actually want to test
    childFixture = TestBed.createComponent(ChildComponent);
  });
});

更改hostFixture.componentInstance 中的model 值实际上不会更改childFixture.componentInstancedata 输入值;这就是我意识到有两个子组件实例的原因。

我的问题很简单,我怎样才能让childFixture 引用在HostComponent 模板中找到的组件夹具,而不是像我目前拥有的不同的实例?

The docs 没有帮助。

【问题讨论】:

  • 一般情况下你可以使用父组件fixture访问子视图。为什么需要子组件fixture?
  • @AmitChigadani 因为我有兴趣测试组件的属性和方法,以响应主机模板中设置的输入值的变化。一个例子是测试主机和孩子之间的双向数据绑定。
  • 文档为什么没有帮助?这正是那里描述的场景。

标签: angular unit-testing


【解决方案1】:

the guide中所述,宿主组件实例是用TestBed.createComponent创建的,子组件实例可以从debugElementBy helper中选择:

childDebugElement = hostFixture.debugElement.query(By.directive(ChildComponent));

或者:

childDebugElement = hostFixture.debugElement.query(By.css('child'));

【讨论】:

  • 你的代码不会给我一个 debugElement 吗?我正在尝试获取组件实例本身,而不是模板元素。
  • 不是模板元素。这是angular.io/api/core/DebugElement。您可以通过childDebugElement.componentInstance 获取实例。
  • 我想访问组件属性。对我来说childDebugElement.context 在这里成功了。 +1 指导我找到解决方案。谢谢!
  • 必须包含更多的 By.css('app-child') 前缀
  • 如果其他人有问题,By 就这样不可用,这里是导入语句:import { By } from '@angular/platform-browser';
【解决方案2】:

上面的答案很好,回答了正文的问题,但问题的标题/标题提出了其他问题。我也想回答标题提出的问题。 Estus 的回答对于特定用例是正确的,但 Google 会根据标题中的问题将您带到这里。

获取子组件而不是原生元素:

测试组件(问题中称为 HostComponent):&lt;child [data]="model" #child&gt;&lt;/child&gt;

然后在类定义中:

@Component({template: `<child #child [data]="model"></child>`})
class HostComponent {
    public model:any;
    @ViewChild('child') child;
}

最后,在测试时,根据规范:

it('should do something', () => {
    component.child.value
    component.child.method
    // etc.
}

您可以在测试用例中使用它来查找您真正要测试的子组件。


剩下的就是满足 cmets 中提出的一个有争议的方面。

在可能的情况下,也有充分的理由将事情设为私有。如果您想对其进行单元测试,我不确定我的感受。如果你想测试一个私有成员,你必须让你想要的带有私有成员的对象的 typescript 编译器可以公开访问,方法是将它转换为并将它包装在括号中,以明确你正在转换的内容。

在组件中:

...
    @ViewChild('child') private child;
...

在测试中:

...
    (<any>component).child.value
    (<any>component).child.method
...

【讨论】:

  • 我尝试了你提供的规范,但我得到了Cannot read property 'next' of undefined。在这里,next 是我尝试访问的方法名称。
  • @RohitJindal,你在调用下一个方法是什么?
  • @ViewChild('child') child; 应该是私有的
  • @NagRock,谢谢。我以前没听说过。你有文件支持吗?
【解决方案3】:

遍历 debugElement(s) 的子节点并访问上下文属性以访问组件及其属性

let debugElement = hostFixture.debugElement.childNodes[x] as DebugElement
debugElement = debugElement.childNodes[x] as DebugElement
...
let component = debugElement.context as YourComponent

这是一种非常静态的方法,因为如果添加了新子节点,那么您可能访问了错误的子节点。最好写一个辅助方法,遍历childNodes并找到正确的名称。

【讨论】:

  • 感谢分享。接受的答案对我来说更有意义,因为在这种情况下,我可以使用hostFixture.debugElement.query(By.directive(ChildComponent)) 直接查询该组件。我就是这么做的
【解决方案4】:

要访问子组件实例(甚至已键入):

const childComponent: ChildComponent = hostFixture.debugElement.query(By.directive(ChildComponent)).componentInstance;

【讨论】:

  • 帮助我检查子组件中收到的输入。谢谢!!
猜你喜欢
  • 1970-01-01
  • 2020-02-12
  • 2018-05-14
  • 1970-01-01
  • 2018-06-06
  • 2019-01-02
  • 2022-06-11
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多