使用 Angular 进行单元测试:如何测试 parent 到 child 的更改
Unit testing with Angular: how to test changes on parent to child
我正在使用 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
})
}));
我想测试 parent 中的更改是否会反映在 child 中,就像在真实场景中发生的那样。
我已经测试过当一切开始时值是相等的,但我想测试当一个人改变 parent 值时的场景,并且应该自动反映在 child.
it('contacts should be the same', () => {
expect(childComponents([0].contacts)
.toEqual(contactsComponent.contacts);
});
错误:
Expected $.length = 0 to equal 1.
Expected $[0] = undefined to equal Object({ id: 1, name: 'Jason Pipemaker' }).
我的解读:不是等更新测试
也许连单元测试都算不上?由于我要测试绑定,所以可能是集成测试。
到目前为止的建议
建议 1:我会尝试 contactsComponent.contacts = JSON.parse(JSON.stringify(contactsList))
评论:谢谢,它不起作用。看到我一开始就测试过了:一切通过。问题是。为什么它在 beginning/launching 有效,但在开始后却无效?我想这是一个以某种方式确保在测试之前一切都完成的问题:一个异步问题,而不是 JSON.stringify。我不知道如何用代码来做到这一点!简单来说:我正在 parent 上更改某些内容,并希望确保 child 收到更改,就像在真实场景中发生的那样。感谢您的参与!
我的 HTML:
<app-contact-list [contacts]=contacts></app-contact-list>
PS。 app-contact-list 是 child:
export class ContactListComponent implements OnInit {
.......
@Input('contacts') contacts: Contact[];//the input for the component
建议2:我认为你应该单独测试组件。
评论:
关于模拟 child,我已经在使用 ng-mock
进行模拟
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ContactsComponent,
MockComponent(ContactListComponent)],
}).compileComponents();
}));
缺点:一些组织不允许安装这些软件包,这对我来说是私人课程,因此,应该按照建议手动模拟。
建议 3:您可以利用 async/await 语法避免它。
评论:试过,但没有用,和以前一样的问题。似乎代码在测试之前正在执行。我在玩笑话时遇到了同样的问题,学到了一个以前从未发生过的技巧。我想我遗漏了类似的东西,确保测试在测试之前等待最终修改的东西。
代码:一些人建议提供代码。
开始了:https://github.com/JorgeGuerraPires/testing-angular-applications/tree/master/website/src/app/contacts
也许有人可以按照建议制作 StackBlitz!
最终解
为了关闭这个问题,我决定在一个简单的案例中进行测试,因为这样测试的核心思想将是唯一的关注点。查看完整的简单应用程序 here before testing. And see the final testing file here。
我从两个答案中得出了见解,并接受了对我帮助最大的那个,甚至评论也有帮助。
谢谢大家!
我认为你应该单独测试组件。在您的 ContactListComponent
中,测试它是否包含正确的联系人。类似于下面的简单测试。测试 child 是一个完全不同的测试,所以模拟 child 组件。
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
。这可以通过打桩 parent 并测试 child 是否反映了正确的数据来完成。
示例:
@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
.
而不是模拟 child 组件
TestBed.configureTestingModule({
schemas: [ CUSTOM_ELEMENTS_SCHEMA ],
...
});
这将防止 angular 在找不到组件时引发错误。谨慎使用,因为这可能会给人造成一切都很好的错误印象。
我喜欢这种测试绑定的方法,并且在我的代码中也这样做。提供的测试几乎是正确的。只是异步性的一个小问题。 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
}));
我正在使用 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
})
}));
我想测试 parent 中的更改是否会反映在 child 中,就像在真实场景中发生的那样。
我已经测试过当一切开始时值是相等的,但我想测试当一个人改变 parent 值时的场景,并且应该自动反映在 child.
it('contacts should be the same', () => {
expect(childComponents([0].contacts)
.toEqual(contactsComponent.contacts);
});
错误:
Expected $.length = 0 to equal 1. Expected $[0] = undefined to equal Object({ id: 1, name: 'Jason Pipemaker' }).
我的解读:不是等更新测试
也许连单元测试都算不上?由于我要测试绑定,所以可能是集成测试。
到目前为止的建议
建议 1:我会尝试 contactsComponent.contacts = JSON.parse(JSON.stringify(contactsList))
评论:谢谢,它不起作用。看到我一开始就测试过了:一切通过。问题是。为什么它在 beginning/launching 有效,但在开始后却无效?我想这是一个以某种方式确保在测试之前一切都完成的问题:一个异步问题,而不是 JSON.stringify。我不知道如何用代码来做到这一点!简单来说:我正在 parent 上更改某些内容,并希望确保 child 收到更改,就像在真实场景中发生的那样。感谢您的参与! 我的 HTML:
<app-contact-list [contacts]=contacts></app-contact-list>
PS。 app-contact-list 是 child:
export class ContactListComponent implements OnInit {
.......
@Input('contacts') contacts: Contact[];//the input for the component
建议2:我认为你应该单独测试组件。
评论: 关于模拟 child,我已经在使用 ng-mock
进行模拟beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ContactsComponent,
MockComponent(ContactListComponent)],
}).compileComponents();
}));
缺点:一些组织不允许安装这些软件包,这对我来说是私人课程,因此,应该按照建议手动模拟。
建议 3:您可以利用 async/await 语法避免它。
评论:试过,但没有用,和以前一样的问题。似乎代码在测试之前正在执行。我在玩笑话时遇到了同样的问题,学到了一个以前从未发生过的技巧。我想我遗漏了类似的东西,确保测试在测试之前等待最终修改的东西。
代码:一些人建议提供代码。 开始了:https://github.com/JorgeGuerraPires/testing-angular-applications/tree/master/website/src/app/contacts
也许有人可以按照建议制作 StackBlitz!
最终解
为了关闭这个问题,我决定在一个简单的案例中进行测试,因为这样测试的核心思想将是唯一的关注点。查看完整的简单应用程序 here before testing. And see the final testing file here。
我从两个答案中得出了见解,并接受了对我帮助最大的那个,甚至评论也有帮助。
谢谢大家!
我认为你应该单独测试组件。在您的 ContactListComponent
中,测试它是否包含正确的联系人。类似于下面的简单测试。测试 child 是一个完全不同的测试,所以模拟 child 组件。
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
。这可以通过打桩 parent 并测试 child 是否反映了正确的数据来完成。
示例:
@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 在找不到组件时引发错误。谨慎使用,因为这可能会给人造成一切都很好的错误印象。
我喜欢这种测试绑定的方法,并且在我的代码中也这样做。提供的测试几乎是正确的。只是异步性的一个小问题。 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
}));