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

Pau*_*tte 19 unit-testing typescript karma-jasmine angular2-testing angular

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

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

我正在尝试为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);
        });
    });
Run Code Online (Sandbox Code Playgroud)

我遇到问题的部分是更改底层模型的值comp.envFilter = 'env3';不会触发更改方法.我补充说el.triggerEventHandler('change', {});但是这个抛出了Failed: Uncaught (in promise): ReferenceError: By is not defined.我在文档中找不到任何提示......任何想法?

Pau*_*tha 22

至于错误.看起来你只需要导入By.这不是全球性的.它应该从以下模块导入

import { By } from '@angular/platform-browser';
Run Code Online (Sandbox Code Playgroud)

就测试部分而言,这是我能够弄清楚的.更改组件中的值时,需要触发更改检测以更新视图.你这样做fixture.detectChanges().完成此操作后,通常应使用值更新视图.

从测试类似于你的例子的东西来看,情况似乎并非如此.在更改检测之后,似乎仍有一些异步任务正在进行.假设我们有以下内容

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');
Run Code Online (Sandbox Code Playgroud)

这似乎不起作用.看起来有一些异步会导致值尚未设置.所以我们需要通过调用等待异步任务fixture.whenStable

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

以上可行.但是现在我们需要触发更改事件,因为这不会自动发生.

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

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

现在我们从事件中获得了另一个异步任务.所以我们需要再次稳定它

以下是我测试过的完整测试.它是源代码集成测试中的示例的重构.他们使用fakeAsynctick使用async和类似whenStable.但是fakeAsync,你不能使用templateUrl,所以我虽然最好重构它来使用async.

此外,源代码测试还进行了双向单向测试,首先测试模型进行查看,然后查看模型.虽然看起来你的测试试图做一种双向测试,从模型到模型.所以我重构了一下,以便更好地适应你的例子.

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');
      });
    });
  }));
});
Run Code Online (Sandbox Code Playgroud)


Cla*_*sos 7

从角度来源(template_integration_spec.ts)看这个例子

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



  it('with option values that are objects', fakeAsync(() => {
       const fixture = TestBed.createComponent(NgModelSelectForm);
       const comp = fixture.componentInstance;
       comp.cities = [{'name': 'SF'}, {'name': 'NYC'}, {'name': 'Buffalo'}];
       comp.selectedCity = comp.cities[1];
       fixture.detectChanges();
       tick();

       const select = fixture.debugElement.query(By.css('select'));
       const nycOption = fixture.debugElement.queryAll(By.css('option'))[1];

       // model -> view
       expect(select.nativeElement.value).toEqual('1: Object');
       expect(nycOption.nativeElement.selected).toBe(true);

       select.nativeElement.value = '2: Object';
       dispatchEvent(select.nativeElement, 'change');
       fixture.detectChanges();
       tick();

       // view -> model
       expect(comp.selectedCity['name']).toEqual('Buffalo');
     }));
Run Code Online (Sandbox Code Playgroud)

  • 请解释为什么这个例子回答了这个问题。 (2认同)
  • 这个答案很有帮助,尤其是“ 2:对象”部分,但我不知道使用它的原因。 (2认同)

Iai*_*ain 7

我发现peeskillet的答案非常有用,但遗憾的是它有点过时,因为发送事件的方式已经改变.我还发现有一个不必要的调用whenStable().所以这是使用peeskillet设置的更新测试:

    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(() => {
            select.nativeElement.dispatchEvent(new Event('change'));
            fixture.detectChanges();
            expect(comp.onSelected).toHaveBeenCalledWith({name : 'NYC'});
            console.log('after expect NYC');
        });
    }));
Run Code Online (Sandbox Code Playgroud)