Angular2 NgModel在茉莉花测试中未获得价值 [英] Angular2 NgModel not getting value in Jasmine test
问题描述
我正在Angular 2中使用模板驱动的表单,并且我尝试先进行测试来开发它们.我已经搜索了该站点以及互联网的其余部分,并且我已经尝试了所有我能找到的所有内容(主要是在fakeAsync中到处都是一堆tick()语句和detectChanges()),以便将NgModel附加到我的输入中以进行提取该值,以便可以将其传递给我的onSubmit函数.输入元素的值设置正确,但是NgModel永不更新,这意味着onSubmit函数无法从NgModel获取正确的值.
I am using template-driven forms in Angular 2, and I'm trying to develop them test-first. I've scoured this site and the rest of the internet and I've tried basically everything I can find (mainly bunches of tick() statements and detectChanges() everywhere in a fakeAsync) to get the NgModel attached to my input to pick up the value so it can be passed to my onSubmit function. The value of the input element sets properly, but the NgModel never updates, which then means the onSubmit function does not get the correct value from the 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');
}));
});
});
我确定我缺少基本的/简单的东西,但是过去的一天我一直在梳理我能找到的所有东西,但是没有运气.
I'm sure I'm missing something basic/simple, but I've spent the past day combing through everything I can find with no luck.
我认为也许人们专注于错误的事情.在这一点上,我非常确定我缺少有关ngForm和ngModel的工作原理的一些基本知识.当我添加
I think maybe people are focusing on the wrong thing. I'm pretty sure at this point that I'm missing something basic about how ngForm and ngModel work. When I add
<p>{{cwf.value | json}}</p>
进入表单,它仅显示{}.我相信它应该显示代表输入的成员属性.如果我在该字段中键入,则该值不会更改.如果我尝试绑定到skillCountFld,也会发生类似的情况.因此,我认为基本表单设置在某种程度上是不正确的,并且在将输入正确连接到skillCountFld控制器之前,该测试永远无法进行.我只是看不到我所缺少的.
into the form, it just shows {}. I believe it should show a member property representing the input. If I type into the field, the value does not change. Similar things happen if I try to bind to skillCountFld. So I think the basic form setup is incorrect somehow, and the test is never going to work until the input is correctly wired to the skillCountFld controller. I just don't see what I'm missing.
推荐答案
在Angular站点上有很多成功的测试 无需等待whenStable > https: //github.com/angular/angular/blob/874243279d5fd2bef567a13e0cef8d0cdf68eec1/modules/%40angular/forms/test/template_integration_spec.ts#L1043
There are a lot of tests at the Angular site that are successfully setting this without waiting for whenStable https://github.com/angular/angular/blob/874243279d5fd2bef567a13e0cef8d0cdf68eec1/modules/%40angular/forms/test/template_integration_spec.ts#L1043
这是因为在beforeEach
中触发fixture.detectChanges();
时,这些测试中的所有代码都在fakeAsync
区域内执行.因此,fakeAsync
区域不了解其范围之外的异步操作.第一次调用detectChanges
时,ngModel
初始化
That's because all code in those tests is executed inside fakeAsync
zone while you are firing fixture.detectChanges();
within beforeEach
. So fakeAsync
zone doesn't know about async operation outside its scope. When you're calling detectChanges
first time ngModel
is initialized
NgModel.prototype.ngOnChanges = function (changes) {
this._checkForErrors();
if (!this._registered)
this._setUpControl(); //<== here
并为输入事件获取正确的回调
and gets right callback for input event
NgForm.prototype.addControl = function (dir) {
var _this = this;
resolvedPromise.then(function () { // notice async operation
var container = _this._findContainer(dir.path);
dir._control = (container.registerControl(dir.name, dir.control));
setUpControl(dir.control, dir); // <== here
在setUpControl
内部,您可以看到将由input
事件调用的函数
inside setUpControl
you can see function that will be called by input
event
dir.valueAccessor.registerOnChange(function (newValue) {
dir.viewToModelUpdate(newValue);
control.markAsDirty();
control.setValue(newValue, { emitModelToViewChange: false });
});
1)因此,如果将fixture.detectChanges
从beforeEach
移至测试,则它应该可以工作:
1) So if you move fixture.detectChanges
from beforeEach
to your test then it should work:
it('submits the value', fakeAsync(() => {
spyOn(component, 'showWorkout').and.callThrough();
fixture.detectChanges();
skillCount = element.query(By.css('#skillCount')).nativeElement;
submitButton = element.query(By.css('#buildWorkout')).nativeElement;
tick();
skillCount.value = '10';
dispatchEvent(skillCount, 'input');
fixture.detectChanges();
submitButton.click();
fixture.detectChanges();
expect(component.showWorkout).toHaveBeenCalledWith('10');
}));
Plunker Example
但是此解决方案似乎非常复杂,因为您需要重写代码才能在每个it
语句中移动fixture.detectChanges
(并且skillCount
,submitButton
等也存在问题)
But this solution seems very complicated since you need to rewrite your code to move fixture.detectChanges
in each of your it
statements (and there is also a problem with skillCount
, submitButton
etc)
2)正如Dinistro所说,async
与whenStable
一起也应为您提供帮助:
2) As Dinistro said async
together with whenStable
should also help you:
it('submits the value', async(() => {
spyOn(component, 'showWorkout').and.callThrough();
fixture.whenStable().then(() => {
skillCount.value = '10';
dispatchEvent(skillCount, 'input');
fixture.detectChanges();
submitButton.click();
fixture.detectChanges();
expect(component.showWorkout).toHaveBeenCalledWith('10');
})
}));
Plunker Example
但是等一下为什么我们必须更改代码?
but wait why do we have to change our code?
3)只需在您的beforeEach函数中添加async
3) Just add async
to your beforeEach function
beforeEach(async(() => {
fixture = TestBed.createComponent(StartworkoutComponent);
component = fixture.componentInstance;
element = fixture.debugElement;
fixture.detectChanges();
}));
Plunker Example
这篇关于Angular2 NgModel在茉莉花测试中未获得价值的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!