Angular2 NgModel在茉莉花测试未获得价值
问题说明
我正在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 #cwf="ngForm" (ngSubmit)="showWorkout(skillCountFld)" novalidate>
<input name="skillCount" #skillCountFld="ngModel" ngModel />
<button type="submit" >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/@angular/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/@angular/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
这篇好文章是转载于:学新通技术网
- 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
- 本站站名: 学新通技术网
- 本文地址: /reply/detail/tanhchaege
-
YouTube API 不能在 iOS (iPhone/iPad) 工作,但在桌面浏览器工作正常?
it1352 07-30 -
保持在后台运行的 iPhone 应用程序完全可操作
it1352 07-25 -
iPhone,一张图像叠加到另一张图像上以创建要保存的新图像?(水印)
it1352 07-17 -
使用 iPhone 进行移动设备管理
it1352 07-23 -
在android同时打开手电筒和前置摄像头
it1352 09-28 -
扫描 NFC 标签时是否可以启动应用程序?
it1352 08-02 -
检查邮件是否发送成功
it1352 07-25 -
Android微调工具-删除当前选择
it1352 06-20 -
希伯来语的空格句子标记化错误
it1352 06-22 -
复制文件夹/文件而不修改属性?
it1352 07-15