Angular 4单元测试表具有双向绑定。从视图

时间:2017-08-30 17:13:04

标签: angular unit-testing asynchronous

我偶然发现了一个奇怪的问题。我使用基于模板的表单和双向绑定执行单元测试。这是测试代码:

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>标记并仅保留视图中的输入。测试正在通过。

我认为我对异步行为做错了。有人有想法吗?

2 个答案:

答案 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中声明你的组件。