使用 fakeAsync()、NgModel 和 detectChanges() 进行测试时出现意外行为
Unexpected behaviour in test with fakeAsync(), NgModel and detectChanges()
我的组件的视图必须显示一些从组件数据生成的复选框(并由 [(NgModel)] 有条件地检查和绑定)。
<div *ngFor="let checkableColumn of visibleCatalog">
<label nz-checkbox [(ngModel)]="checkableColumn.checked">
{{ checkableColumn.column.label | titlecase }}
</label>
</div>
我知道 NgModel 更新是异步的,所以我将测试放在 fakeAsnyc 上下文中以简化测试。
我的第一次尝试是:
it('should update his checkboxes', fakeAsync(() => {
component.ngOnChanges({}); // For populating my data
fixture.detectChanges();
tick();
const checkboxesChecked = document.querySelectorAll('input[type=checkbox]:checked');
expect(checkboxesChecked.length).withContext('1 checkbox should be checked').toBe(1);
}));
但是没有用,没有选中复选框,我发现下面的是:
it('should update his checkboxes', fakeAsync(() => {
component.ngOnChanges({}); // For populating my data
fixture.detectChanges(); // Added
tick();
fixture.detectChanges();
const checkboxesChecked = document.querySelectorAll('input[type=checkbox]:checked');
expect(checkboxesChecked.length).withContext('1 checkbox should be checked').toBe(1);
}));
我不明白为什么我需要在 tick() 之前更新我的视图..
有谁理解这种行为?
这是一个很好但棘手的问题。
从 angular documentation on testing 看来,第一个 detectChanges 仅通过 onInit()。为了查看您的异步更改,您需要再次检查勾号和 运行,因为要更新的异步过程尚未开始(它仅在组件初始化后开始)。
it('should display error when TwainService fails', fakeAsync(() => {
// tell spy to return an error observable
getQuoteSpy.and.returnValue(
throwError('TwainService test failure'));
fixture.detectChanges(); // onInit()
// sync spy errors immediately after init
tick(); // flush the component's setTimeout()
fixture.detectChanges(); // update errorMessage within setTimeout()
expect(errorMessage()).toMatch(/test failure/, 'should display error');
expect(quoteEl.textContent).toBe('...', 'should show placeholder');
}));
关于同步情况 angular 声明如下:
Because the spy result returns synchronously, the getQuote() method
updates the message on screen immediately after the first change
detection cycle during which Angular calls ngOnInit.
You're not so lucky when testing the error path. Although the service
spy will return an error synchronously, the component method calls
setTimeout(). The test must wait at least one full turn of the
JavaScript engine before the value becomes available. The test must
become asynchronous.
我的组件的视图必须显示一些从组件数据生成的复选框(并由 [(NgModel)] 有条件地检查和绑定)。
<div *ngFor="let checkableColumn of visibleCatalog">
<label nz-checkbox [(ngModel)]="checkableColumn.checked">
{{ checkableColumn.column.label | titlecase }}
</label>
</div>
我知道 NgModel 更新是异步的,所以我将测试放在 fakeAsnyc 上下文中以简化测试。
我的第一次尝试是:
it('should update his checkboxes', fakeAsync(() => {
component.ngOnChanges({}); // For populating my data
fixture.detectChanges();
tick();
const checkboxesChecked = document.querySelectorAll('input[type=checkbox]:checked');
expect(checkboxesChecked.length).withContext('1 checkbox should be checked').toBe(1);
}));
但是没有用,没有选中复选框,我发现下面的是:
it('should update his checkboxes', fakeAsync(() => {
component.ngOnChanges({}); // For populating my data
fixture.detectChanges(); // Added
tick();
fixture.detectChanges();
const checkboxesChecked = document.querySelectorAll('input[type=checkbox]:checked');
expect(checkboxesChecked.length).withContext('1 checkbox should be checked').toBe(1);
}));
我不明白为什么我需要在 tick() 之前更新我的视图.. 有谁理解这种行为?
这是一个很好但棘手的问题。
从 angular documentation on testing 看来,第一个 detectChanges 仅通过 onInit()。为了查看您的异步更改,您需要再次检查勾号和 运行,因为要更新的异步过程尚未开始(它仅在组件初始化后开始)。
it('should display error when TwainService fails', fakeAsync(() => {
// tell spy to return an error observable
getQuoteSpy.and.returnValue(
throwError('TwainService test failure'));
fixture.detectChanges(); // onInit()
// sync spy errors immediately after init
tick(); // flush the component's setTimeout()
fixture.detectChanges(); // update errorMessage within setTimeout()
expect(errorMessage()).toMatch(/test failure/, 'should display error');
expect(quoteEl.textContent).toBe('...', 'should show placeholder');
}));
关于同步情况 angular 声明如下:
Because the spy result returns synchronously, the getQuote() method updates the message on screen immediately after the first change detection cycle during which Angular calls ngOnInit.
You're not so lucky when testing the error path. Although the service spy will return an error synchronously, the component method calls setTimeout(). The test must wait at least one full turn of the JavaScript engine before the value becomes available. The test must become asynchronous.