【问题标题】:Unit testing with Angular: how to test changes on parent to child使用 Angular 进行单元测试:如何测试父子之间的更改
【发布时间】:2021-09-20 15:08:59
【问题描述】:

我正在使用 Jasmine 在 Angular 中对组件进行单元测试。 我的测试

it('contacts should be passed to child component', fakeAsync(() => {
    const newContact: Contact = {
        id: 1,
        name: 'Jason Pipemaker'
    };
    const contactsList: Array<Contact> = [newContact];
    contactsComponent.contacts = contactsList;//change on parent component
    tick()
    fixture.detectChanges();
    fixture.whenStable().then(() => {
        expect(childComponents()[0].contacts).toEqual(contactsComponent.contacts);//want to check if changed on child also
    })
}));

我想测试父母的变化是否会反映在孩子身上,就像在真实场景中发生的那样。

我已经测试了当一切开始时值是相等的,但是我想测试当一个人改变父值时的场景,这应该会自动反映在孩子身上。

it('contacts should be the same', () => {
   expect(childComponents([0].contacts)
      .toEqual(contactsComponent.contacts);
});

错误:

预期 $.length = 0 等于 1。 预期 $[0] = undefined 等于 Object({ id: 1, name: 'Jason Pipemaker' })。

我的解读:不等更新测试。

也许它甚至不是单元测试?既然要测试绑定,可能是集成测试吧。

目前的建议

建议 1:我会尝试 contactsComponent.contacts = JSON.parse(JSON.stringify(contactsList))

cmets:谢谢,它不起作用。看到我一开始就已经测试过了:一切都通过了。问题是。为什么它在开始/启动时起作用,但在启动后不起作用?我想这是一个在测试之前以某种方式确保一切都完成的问题:一个异步问题,而不是 JSON.stringify。我不知道如何用代码来做到这一点!简单来说:我正在更改父母的某些内容,并希望确保孩子收到更改,就像它发生在真实场景中一样。感谢您的参与! ????????? 我的 HTML:

<app-contact-list [contacts]=contacts></app-contact-list>

PS。 app-contact-list 是孩子:

export class ContactListComponent implements OnInit {
  .......
  @Input('contacts') contacts: Contact[];//the input for the component

建议 2:我认为您应该单独测试组件。

评论: 关于嘲笑孩子,我已经在用 ng-mock 嘲笑了

beforeEach(async(() => {
   TestBed.configureTestingModule({
      declarations: [ContactsComponent, 
         MockComponent(ContactListComponent)],
   }).compileComponents();
}));

缺点:一些组织不允许安装这些软件包,因此,应该按照建议手动模拟。

建议 3:您可以通过利用 async/await 语法来避免它。

评论:试过了,但没用,和以前一样的问题。似乎代码在测试之前正在执行。我遇到了与 Jest 相同的问题,学到了一个以前从未发生过的技巧。我想我错过了类似的东西,确保测试在测试之前等待最终修改。

代码:一些人建议提供代码。 在这里:https://github.com/JorgeGuerraPires/testing-angular-applications/tree/master/website/src/app/contacts

也许有人可以按照建议制作 StackBlitz!????????????

最终解决方案

为了结束这个问题,我决定在一个简单的案例中进行测试,这样被测试的核心思想将是唯一的关注点。测试前查看完整的简单应用程序here。并查看最终测试文件here

我从这两个答案中汲取了见解,并接受了对我帮助最大的一个,即使是 cmets 也有帮助。

谢谢大家!

【问题讨论】:

  • 我会尝试使用contactsComponent.contacts = JSON.parse(JSON.stringify(contactsList))
  • 谢谢,它不起作用。看到我一开始就已经测试过了:一切都通过了。问题是。为什么它在开始/启动时起作用,但在启动后不起作用?我想这是一个在测试之前以某种方式确保一切都完成的问题:一个异步问题,而不是 JSON.stringify。我不知道如何用代码来做到这一点!感谢您的参与! ????????????
  • 如果你能做一个简单的Stackblitz 将是完美的分析!
  • 我没有时间详细回答,但您可以使用 fixture.detectChanges() 在 TestBed 中强制渲染。根据您的具体情况,您可能需要将其与fakeAsync 块和tick()asyncwhenStable() 结合使用。也就是说,我对单元测试的偏好是尽可能地模拟依赖项。孩子应该可以独立于父母进行测试,反之亦然。对于测试全屏——而不是组件——我更喜欢 E2E 测试。
  • 对于此类测试,角度具有angular.io/guide/… 技术。为什么你需要 tick 和 fakeAsync 呢?

标签: angular unit-testing tdd karma-jasmine


【解决方案1】:

我认为您应该单独测试该组件。在您的ContactListComponent 中,测试它是否包含正确的联系人。类似于下面的简单测试。测试子组件是完全不同的测试,因此请模拟子组件。

  it('have the correct contacts', () => {
    expect(component.contacts[0].id).toBe(1);
    expect(component.contacts[0].name).toBe('value');
  });

简单的模拟示例:

@Component({
  selector: 'child-component'
  template: ''
})
class TestChildComponent {
  @Input() contact;
}

现在,您想测试您的ChildComponent。这可以通过对父母进行存根并测试孩子是否反映正确的数据来完成。

例子:

@Component({
  template: '<child-component [contact]="contact"></child-component>'
})
class TestHostComponent {
  contact = new Contact();
  @ViewChild(ChildComponent) component: ChildComponent;
}

describe('ChildComponent', () => {
  let component: ChildComponent;
  let host: TestHostComponent;
  let fixture: ComponentFixture<TestHostComponent>;


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

  beforeEach(() => {
    fixture = TestBed.createComponent(TestHostComponent);
    loader = TestbedHarnessEnvironment.loader(fixture);
    fixture.detectChanges();

    host = fixture.componentInstance;
    component = host.component;
    fixture.detectChanges();
  });

  it('should create', () => {
    expect(component).toBeTruthy();
  });

  it('should be able to present a contact', () => {
    host.contact = new Contact('otherValue');
    expect(SomeQuery.value).toBe('otherValue')
  }); 

});

编辑:

您可以添加CUSTOM_ELEMENTS_SCHEMA,而不是模拟子组件。

TestBed.configureTestingModule({
  schemas: [ CUSTOM_ELEMENTS_SCHEMA ],
  ...
});

这将防止 Angular 在找不到组件时引发错误。谨慎使用,因为这可能会给人一种错误的印象,一切都很好。

【讨论】:

  • 谢谢,我会看看,很快就会回来找你! ???
  • 您似乎手动创建了模拟。这个库怎么样:google.com/…
  • 我用过,很喜欢。唯一的缺点是一些公司有政策禁止安装这些软件包。
  • 这里遇到问题 ``` import { TestbedHarnessEnvironment } from "@angular/cdk/testing" ``` 做了一些研究,没有找到解决办法。这引起了我的注意:github.com/angular/components/issues/5803
  • 这个库好像不是公开的。
【解决方案2】:

我喜欢这种测试绑定的方法,并且在我的代码中也这样做。提供的测试几乎是正确的。只是异步的一个小问题。 fixture.whenStable().then( 中的代码将在测试被 karma 视为完成后执行。您可以利用async/await 语法来避免它。

it('contacts should be passed to child component', aynsc () => {
    const newContact: Contact = {
        id: 1,
        name: 'Jason Pipemaker'
    };
    const contactsList: Array<Contact> = [newContact];
    contactsComponent.contacts = contactsList;//set on parent component

    fixture.detectChanges();
    await fixture.whenStable();

    expect(childComponents()[0].contacts)
       .toEqual(contactsComponent.contacts);//want to check if changed on child also
}));

it('child component should receive data when data in parent is changed', aynsc () => {
    const firstContact: Contact = {
        id: 1,
        name: 'Jason Pipemaker'
    };
    const contactsList: Array<Contact> = [firstContact];
    contactsComponent.contacts = contactsList;//set on parent component
    fixture.detectChanges();
    await fixture.whenStable();
    const secondContact: Contact = {
        id: 1,
        name: 'Jason Pipemaker'
    };

    const contactsList2: Array<Contact> = [firstContact, secondContact];
    contactsComponent.contacts = contactsList2;//change on parent component
    fixture.detectChanges();
    await fixture.whenStable();

    expect(childComponents()[0].contacts)
       .toEqual(contactsComponent.contacts);//want to check if changed on child also
}));

【讨论】:

  • 你好,谢谢你的建议,已经测试过了,同样的问题。测试不等待被测试。谢谢!你说的有道理,我也是这么认为的。我用 Jest 遇到了这个问题,直到我学会了 Jest 的两个技巧,它再也没有发生过。我错过了一些东西。我将尝试在一个更简单的问题上测试您的技术,也许问题是我的代码。我会告诉你的。
  • 你能准备一个简单的例子吗?好像不难?我也试试!
  • 对不起,我没有时间准备一个例子。很高兴知道你解决了它
猜你喜欢
  • 2019-11-02
  • 2023-01-13
  • 1970-01-01
  • 2012-01-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-10-07
  • 1970-01-01
相关资源
最近更新 更多