Angular2 NgModel 在 Jasmine 测试中没有获得价值

2023-12-20

我在 Angular 2 中使用模板驱动的表单,并且尝试以测试优先的方式开发它们。我已经搜索了这个网站和互联网的其余部分,并且基本上尝试了我能找到的所有内容(主要是 fakeAsync 中到处都是的 tick() 语句和 detectorChanges() ),以将 NgModel 附加到我的输入以获取值,以便可以将其传递给我的 onSubmit 函数。输入元素的值设置正确,但 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');
    }));
  });
});
  

我确信我错过了一些基本/简单的东西,但我在过去的一天里梳理了我能找到的所有东西,但没有运气。

Edit:

我认为人们可能把注意力集中在错误的事情上。我现在非常确定我缺少一些关于 ngForm 和 ngModel 如何工作的基本知识。当我添加

&lt;p>{{cwf.value | json}}&lt;/p>

进入表单,它只显示{}。我相信它应该显示代表输入的成员属性。如果我在字段中输入内容,该值不会改变。如果我尝试绑定到 SkillCountFld,也会发生类似的情况。所以我认为基本表单设置在某种程度上是不正确的,并且在输入正确连接到 SkillCountFld 控制器之前测试永远不会起作用。我只是不明白我错过了什么。


Angular网站上有很多测试都成功 设置此项无需等待 whenStablehttps://github.com/angular/angular/blob/874243279d5fd2bef567a13e0cef8d0cdf68eec1/modules/%40angular/forms/test/template_integration_spec.ts#L1043 https://github.com/angular/angular/blob/874243279d5fd2bef567a13e0cef8d0cdf68eec1/modules/%40angular/forms/test/template_integration_spec.ts#L1043

那是因为这些测试中的所有代码都在内部执行fakeAsync当你开火时的区域fixture.detectChanges(); within beforeEach. So fakeAsynczone 不知道其范围之外的异步操作。当你打电话时detectChanges第一次ngModel已初始化

 NgModel.prototype.ngOnChanges = function (changes) {
            this._checkForErrors();
            if (!this._registered)
                this._setUpControl(); //<== here

并获取输入事件的正确回调

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

inside setUpControl你可以看到将被调用的函数input event

dir.valueAccessor.registerOnChange(function (newValue) {
  dir.viewToModelUpdate(newValue);
  control.markAsDirty();
  control.setValue(newValue, { emitModelToViewChange: false });
});

1)所以如果你搬家fixture.detectChanges from beforeEach根据您的测试,它应该可以工作:

 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');
}));

笨蛋的例子 https://plnkr.co/edit/NWqtnlcbR6FQwiZ1gpDQ?p=preview

但这个解决方案看起来非常复杂,因为您需要重写代码才能移动fixture.detectChanges在你的每一个it声明(并且还有一个问题skillCount, submitButton etc)

2)正如迪尼斯特罗所说async和...一起whenStable还应该帮助你:

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');
  })
}));

笨蛋的例子 https://plnkr.co/edit/pWZpa1257921xuI1lmfn?p=preview

但是等等,为什么我们必须改变我们的代码呢?

3)只需添加async到你的 beforeEach 函数

beforeEach(async(() => {
  fixture = TestBed.createComponent(StartworkoutComponent);
  component = fixture.componentInstance;
  element = fixture.debugElement;
  fixture.detectChanges();
}));

笨蛋的例子 https://plnkr.co/edit/bRR6qxrqj4cd8zP7iMi5?p=preview

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Angular2 NgModel 在 Jasmine 测试中没有获得价值 的相关文章

随机推荐