温馨提示:本文翻译自stackoverflow.com,查看原文请点击:angular - How to change value of a select box in angular2 unit test?

angular - 如何在angular2单元测试中更改选择框的值?

发布于 2020-03-27 11:59:12

我有一个Angular2组件,其中包含一个看起来像

<select [(ngModel)]="envFilter" class="form-control" name="envSelector" (ngModelChange)="onChangeFilter($event)">
    <option *ngFor="let env of envs" [ngValue]="env">{{env}}</option>
</select>

我正在尝试为ngModelChange事件编写单元测试。这是我最近的失败尝试

it("should filter and show correct items", async(() => {
    fixture.detectChanges();
    fixture.whenStable().then(() => {
        el = fixture.debugElement.query(By.name("envSelector"));
        fixture.detectChanges();
        makeResponse([hist2, longhist]);
        comp.envFilter = 'env3';
        el.triggerEventHandler('change', {});
        fixture.whenStable().then(() => {
            fixture.detectChanges();
            expect(comp.displayedHistory).toEqual(longhist);
        });
    });

我遇到的问题是,更改基础模型的值comp.envFilter = 'env3';不会触发更改方法。我添加了,el.triggerEventHandler('change', {});但是抛出了Failed: Uncaught (in promise): ReferenceError: By is not defined我在文档中找不到任何提示...有什么想法吗?

查看更多

查看更多

提问者
Paul Becotte
被浏览
61
6,245 2018-02-20 10:36

As far as the error. It seems like you just need to import By. This is not something that is global. It should be imported from the following module

import { By } from '@angular/platform-browser';

As far as the testing part, this is what I have been able to figure out. When you change a value in a the component, you need to trigger a change detection to update the view. You do this with fixture.detectChanges(). Once this is done, normally the view should be updated with the value.

From testing something similar to your example, it seems this is not the case though. It seems there is still some asynchronous task going on after the change detection. Say we have the following

const comp = fixture.componentInstance;
const select = fixture.debugElement.query(By.css('select'));

comp.selectedValue = 'a value;
fixture.DetectChanges();
expect(select.nativeElement.value).toEqual('1: a value');

This doesn't seem to work. It appears there is some async going on causing the value not to be set yet. So we need to wait for the async tasks by calling fixture.whenStable

comp.selectedValue = 'a value;
fixture.DetectChanges();
fixture.whenStable().then(() => {
  expect(select.nativeElement.value).toEqual('1: a value');
});

The above would work. But now we need to trigger the change event as that doesn't happen automatically.

fixture.whenStable().then(() => {
  expect(select.nativeElement.value).toEqual('1: a value');

  dispatchEvent(select.nativeElement, 'change');
  fixture.detectChanges();
  fixture.whenStable().then(() => {
    // component expectations here
  });
});

Now we have another asynchronous task from the event. So we need to stabilize it again

Below is a complete test that I tested with. It's a refactor of the example from the source code integration tests. They used fakeAsync and tick which is similar to using async and whenStable. But with fakeAsync, you can't use templateUrl, so I though it would be best to refactor it to use async.

Also the source code tests does kind of a double one way testing, first testing model to view, then view to model. While it looks like your test was trying to do kind of a two-way test, from model around back to model. So I refactored it a bit to suite your example better.

import { Component } from '@angular/core';
import { TestBed, getTestBed, async } from '@angular/core/testing';
import { FormsModule } from '@angular/forms';
import { By } from '@angular/platform-browser';
import { dispatchEvent } from '@angular/platform-browser/testing/browser_util';

@Component({
  selector: 'ng-model-select-form',
  template: `
    <select [(ngModel)]="selectedCity" (ngModelChange)="onSelected($event)">
      <option *ngFor="let c of cities" [ngValue]="c"> {{c.name}} </option>
    </select>
  `
})
class NgModelSelectForm {
  selectedCity: {[k: string]: string} = {};
  cities: any[] = [];

  onSelected(value) {
  }
}

describe('component: NgModelSelectForm', () => {
  beforeEach(() => {
    TestBed.configureTestingModule({
      imports: [ FormsModule ],
      declarations: [ NgModelSelectForm ]
    });
  });

  it('should go from model to change event', async(() => {
    const fixture = TestBed.createComponent(NgModelSelectForm);
    const comp = fixture.componentInstance;
    spyOn(comp, 'onSelected');
    comp.cities = [{'name': 'SF'}, {'name': 'NYC'}, {'name': 'Buffalo'}];
    comp.selectedCity = comp.cities[1];
    fixture.detectChanges();
    const select = fixture.debugElement.query(By.css('select'));

    fixture.whenStable().then(() => {
      dispatchEvent(select.nativeElement, 'change');
      fixture.detectChanges();
      fixture.whenStable().then(() => {
        expect(comp.onSelected).toHaveBeenCalledWith({name : 'NYC'});
        console.log('after expect NYC');
      });
    });
  }));
});

发布
问题

分享
好友

手机
浏览

扫码手机浏览