我偶然发现了一个奇怪的问题。我使用基于模板的表单和双向绑定执行单元测试。这是测试代码:
describe('Template Forms Input', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [BrowserModule, FormsModule],
declarations: [DummyFormsComponent],
}).compileComponents();
});
it('DOM input value changes the component model', fakeAsync(() => {
const fixture = TestBed.createComponent(DummyFormsComponent);
fixture.detectChanges();
const dummyInputDe = fixture.debugElement.query(By.css('input'));
const dummyInputEl = dummyInputDe.nativeElement;
dummyInputEl.value = 'Super dummy';
dummyInputEl.dispatchEvent(new Event('input'));
tick();
fixture.detectChanges();
expect(fixture.debugElement.query(By.css('h2')).nativeElement.textContent).toEqual('Super dummy');
}));
});
@Component({
selector: 'dummy-forms',
template: `
<form>
<input name="title" [(ngModel)]="model.anotherDummyValue">
</form>
<h2>{{model.anotherDummyValue}}</h2>
`
})
class DummyFormsComponent {
model = { anotherDummyValue: '', date: '' };
}
我无法通过测试。 h2总是空的。然而。如果我删除<form>
标记并仅保留视图中的输入。测试正在通过。
我认为我对异步行为做错了。有人有想法吗?
答案 0 :(得分:1)
遇到了与您类似的问题(请在下面参阅我的Angular环境详细信息),搜索了有关单元测试NgModel
的所有Internet。最后,我发现仅将值设置为查询的输入(实际上是通过NgModel
绑定的)是不够的。即dummyInputEl.value = 'Super dummy';
并且您所使用的dispatchEvent('input')
永远不会启动并运行实际的NgModel
操作。
要解决该问题,您必须从绑定的NgModel
获取 injected input
实例,并通过的valueAccessor
属性设置值writeValue
方法, ,然后在DOM本机元素上发出'input'
事件 。
...
const ngModel = dummyInputDe.injector.get(NgModel);
tick(); // <== Important to wait until stable before writing value
ngModel.valueAccessor.writeValue('Super dummy');
dummyInputDe.nativeElement.dispatchEvent(new Event('input'));
fixture.detectChanges(); // <= force updates to the view
expect(fixture.debugElement.query(By.css('h2'))
.nativeElement.textContent).toEqual('Super dummy'); // <= Viola!
...
请注意,在写入tick
值访问器之前,我们必须在fakeAsync
内提供NgModel
。可能是因为NgModel的访问器需要一段时间才能准备好,然后才能在其上写入值(对此表示赞赏反馈)。
对于没有fakeAsync
区域的规范,应使用fixture.whenStable()
解析的承诺处理相同的方案,以获得所需的结果。
注意:
关于网络上的单元测试的每个主题,尤其是在NgModel
受到质疑的情况下,都未曾提及。甚至没有官方的Angular文档。不知道新的Angular版本(尤其是FormsModule
实现中的更改)是否已强制执行此更改。
v5.2.0
v2.0.0
v2.8.0
答案 1 :(得分:0)
在代码中尝试添加组件实例。更多我没有在你的组件中看到ngOnInit或构造函数。
component = fixture.componentInstance;
我们通常以下面的方式进行
describe('EditComponent', () => {
let component: EditComponent;
let fixture: ComponentFixture<EditComponent>;
let displayText: DebugElement;
let editableText: DebugElement;
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [
FormsModule,
ReactiveFormsModule
],
declarations: [ EditComponent, MockCkeditorComponent ]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(EditComponent);
component = fixture.componentInstance;
component.text = 'test input in the editor';
displayText = fixture.debugElement.query(By.css('.row'));
editableText = fixture.debugElement.query(By.css('.editor-form'));
});
it('should be created', () => {
component.ngOnInit();
fixture.detectChanges();
expect(component).toBeTruthy();
});
....
在beforeEach中声明你的组件。