我在Angular 2中使用模板驱动的表单,我正在尝试先测试它们 . 我已经搜索过这个网站以及互联网的其他部分了,我已经尝试了所有我能找到的东西(主要是一串tick()语句和detectChanges()在fakeAsync中的所有地方)以获得附加到我输入的NgModel来获取该值可以传递给我的onSubmit函数 . input元素的值设置正确,但NgModel永远不会更新,这意味着onSubmit函数无法从NgModel获取正确的值 .
这是模板:
<form id="createWorkout" #cwf="ngForm" (ngSubmit)="showWorkout(skillCountFld)" novalidate>
<input name="skillCount" id="skillCount" class="form-control" #skillCountFld="ngModel" ngModel />
<button type="submit" id="buildWorkout">Build a Workout</button>
</form>
注意:我知道发送ngSubmit的值会导致测试失败,但这意味着我可以在函数中设置断点并检查NgModel .
这是组件:
import { Component, OnInit } from '@angular/core';
import {SkillService} from "../model/skill-service";
import {NgModel} from "@angular/forms";
@Component({
selector: 'app-startworkout',
templateUrl: './startworkout.component.html',
styleUrls: ['./startworkout.component.css']
})
export class StartworkoutComponent implements OnInit {
public skillCount:String;
constructor(public skillService:SkillService) { }
showWorkout(value:NgModel):void {
console.log('breakpoint', value.value);
}
ngOnInit() {
}
}
这是规格:
/* tslint:disable:no-unused-variable */
import {async, ComponentFixture, TestBed, fakeAsync, tick} from '@angular/core/testing';
import {By, BrowserModule} from '@angular/platform-browser';
import { DebugElement } from '@angular/core';
import { StartworkoutComponent } from './startworkout.component';
import {SkillService} from "../model/skill-service";
import {Store} from "../core/store";
import {SportService} from "../model/sport-service";
import {FormsModule} from "@angular/forms";
import {dispatchEvent} from "@angular/platform-browser/testing/browser_util";
describe('StartworkoutComponent', () => {
let component: StartworkoutComponent;
let fixture: ComponentFixture;
let element:DebugElement;
let skillService:SkillService;
beforeEach(async(() => {
var storeSpy:any = jasmine.createSpyObj('store', ['getValue', 'storeValue', 'removeValue']);
var stubSkillService:SkillService = new SkillService(storeSpy);
TestBed.configureTestingModule({
declarations: [ StartworkoutComponent ],
providers: [{provide:Store , useValue:storeSpy}, SportService, SkillService],
imports: [BrowserModule, FormsModule]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(StartworkoutComponent);
component = fixture.componentInstance;
element = fixture.debugElement;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
describe('without workout', () => {
let createWorkout:DebugElement;
let skillCount:HTMLInputElement;
let submitButton:HTMLButtonElement;
beforeEach(() => {
createWorkout = element.query(By.css('#createWorkout'));
skillCount = element.query(By.css('#skillCount')).nativeElement;
submitButton = element.query(By.css('#buildWorkout')).nativeElement;
});
it('has createWorkout form', () => {
expect(createWorkout).toBeTruthy();
expect(skillCount).toBeTruthy();
});
it('submits the value', fakeAsync(() => {
spyOn(component, 'showWorkout').and.callThrough();
tick();
skillCount.value = '10';
dispatchEvent(skillCount, 'input');
fixture.detectChanges();
tick(50);
submitButton.click();
fixture.detectChanges();
tick(50);
expect(component.showWorkout).toHaveBeenCalledWith('10');
}));
});
});
我确定我错过了一些基本/简单的东西,但是我花了一整天时间来梳理我能找到的所有东西,没有运气 .
编辑:
我想也许人们会关注错误的事情 . 我很确定在这一点上我遗漏了一些关于ngForm和ngModel如何工作的基本知识 . 当我添加
<p>{{cwf.value | json}}</p>
在表单中,它只显示{} . 我相信它应该显示一个表示输入的成员属性 . 如果我输入字段,则值不会更改 . 如果我尝试绑定到skillCountFld,会发生类似的事情 . 所以我认为基本的表单设置是不正确的,并且在输入正确连接到skillCountFld控制器之前测试永远不会工作 . 我只是看不到我错过的东西 .
2 回答
那是因为当你在
beforeEach
中触发fixture.detectChanges();
时,那些测试中的所有代码都在fakeAsync
区域内执行 . 所以fakeAsync
区域没有't know about async operation outside its scope. When you'重新调用detectChanges
第一次ngModel
被初始化并获得输入事件的正确回调
在
setUpControl
内,您可以看到input
事件将调用的函数1)因此,如果您将
fixture.detectChanges
从beforeEach
移到测试中,那么它应该工作:Plunker Example
但是这个解决方案似乎非常复杂,因为你需要重写你的代码以在每个
it
语句中移动fixture.detectChanges
(并且skillCount
,submitButton
等也存在问题)2)正如Dinistro所说
async
和whenStable
也应该帮助你:Plunker Example
但是等一下我们为什么要改变我们的代码呢?
3)只需将
async
添加到beforeEach函数中Plunker Example
我认为
whenStable
应该在你的情况下做到这一点:更多信息:https://angular.io/docs/ts/latest/guide/testing.html#!#when-stable
EDIT: 如果直接操作DOM元素的值,
ValueAccessor
似乎没有注意到更改 . 但它应该注意到,如果你直接用ValueAccessor
写入值: