Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/unit-testing/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Unit testing 如何更改angular2单元测试中选择框的值?_Unit Testing_Angular_Typescript_Karma Jasmine_Angular2 Testing - Fatal编程技术网

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

Unit testing 如何更改angular2单元测试中选择框的值?,unit-testing,angular,typescript,karma-jasmine,angular2-testing,Unit Testing,Angular,Typescript,Karma Jasmine,Angular2 Testing,我有一个Angular2组件,其中包含一个选择框 <select [(ngModel)]="envFilter" class="form-control" name="envSelector" (ngModelChange)="onChangeFilter($event)"> <option *ngFor="let env of envs" [ngValue]="env">{{env}}</option> </select> 我遇到的问题

我有一个Angular2组件,其中包含一个选择框

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

我遇到的问题是更改基础模型
comp.envFilter='env3'的值不会触发更改方法。我添加了
el.triggerEventHandler('change',{})但这会抛出
失败:未捕获(承诺中):引用错误:未定义。我在文档中找不到任何提示。。。有什么想法吗?

至于错误。似乎您只需要通过
导入
。这不是全球性的。它应该从以下模块导入

import { By } from '@angular/platform-browser';
就测试部分而言,这是我能够理解的。更改组件中的值时,需要触发更改检测以更新视图。您可以使用
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');
这似乎不起作用。似乎有一些异步正在进行,导致尚未设置该值。因此,我们需要通过调用fixture.whenStable来等待异步任务

comp.selectedValue = 'a value';
fixture.DetectChanges();
fixture.whenStable().then(() => {
  expect(select.nativeElement.value).toEqual('1: a value');
});
上述方法可行。但是现在我们需要触发更改事件,因为这不是自动发生的

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

  dispatchEvent(select.nativeElement, 'change');
  fixture.detectChanges();
  fixture.whenStable().then(() => {
    // component expectations here
  });
});
现在,我们从事件中获得了另一个异步任务。所以我们需要再次稳定它

下面是我使用的完整测试。它是来自的示例的重构。他们使用了
fakeAsync
tick
,这与使用
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');
      });
    });
  }));
});
从'@angular/core'导入{Component};
从'@angular/core/testing'导入{TestBed,getTestBed,async};
从'@angular/forms'导入{FormsModule};
从“@angular/platform browser”导入{By}”;
从“@angular/platform browser/testing/browser_util”导入{dispatchEvent};
@组成部分({
选择器:“ng模型选择表单”,
模板:`
{{c.name}}
`
})
类NgModelSelectForm{
selectedCity:{[k:string]:string}={};
城市:任何[]=[];
onSelected(值){
}
}
描述('component:NgModelSelectForm',()=>{
在每个之前(()=>{
TestBed.configureTestingModule({
导入:[FormsModule],
声明:[NgModelSelectForm]
});
});
它('should from model to change event',异步(()=>{
const fixture=TestBed.createComponent(NgModelSelectForm);
常数comp=fixture.componentInstance;
间谍(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()然后(()=>{
dispatchEvent(select.nativeElement,“change”);
fixture.detectChanges();
fixture.whenStable()然后(()=>{
expect(comp.onSelected).tohavencalledwith({name:'NYC'});
log('after expect NYC');
});
});
}));
});

从角度源(模板集成规范ts)查看此示例

@组件({
选择器:“ng模型选择表单”,
模板:`
{{c.name}}
`
})
类NgModelSelectForm{
selectedCity:{[k:string]:string}={};
城市:任何[]=[];
}
它('具有作为对象的选项值',fakeAsync(()=>{
const fixture=TestBed.createComponent(NgModelSelectForm);
常数comp=fixture.componentInstance;
comp.cities=[{'name':'SF'},{'name':'NYC'},{'name':'Buffalo'}];
comp.selectedCity=comp.cities[1];
fixture.detectChanges();
勾选();
const select=fixture.debugElement.query(By.css('select');
const nycOption=fixture.debugElement.queryAll(By.css('option'))[1];
//模型->视图
expect(select.nativeElement.value).toEqual('1:Object');
expect(nycOption.nativeElement.selected).toBe(true);
select.nativeElement.value='2:Object';
dispatchEvent(select.nativeElement,“change”);
fixture.detectChanges();
勾选();
//查看->模型
expect(comp.selectedCity['name')).toEqual('Buffalo');
}));

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

希望这能帮助一些人。与OP提出的问题相同,但代码略有不同

在7号楼工作

HTML:


请解释为什么这个例子回答了这个问题。这个答案很有帮助,特别是
'2:Object'
部分,但我不知道使用它背后的原因。由于语法更改,这个答案的源链接已经过时。请参阅Iain的答案-由于语法更改,此答案已过时。请参考Iain的答案-必须手动执行
。dispatchEvent(新事件('change'))
真是令人敬畏。不过,对这个答案没有任何批评。很高兴我找到了它。谢谢Iain:设置了select.nativeElement.selectedIndex的单元测试如下:“select.nativeElement.dispatchEvent(新事件('change'));”:-)
@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');
     }));
    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');
        });
    }));
<select id="dashboard-filter" class="form-control" name="dashboard-filter" [ngModel]="dashboardFilterValue" (ngModelChange)="onFilterChange($event)"
              [disabled]="disabled">
  <option *ngFor="let filter of dashboardFilters" [ngValue]="filter.value">{{ filter.name }}</option>
</select>
it('onFilterChange', () => {

  // ensure dropdown is enabled
  expect(component.disabled).toBe(false)

  // spies
  spyOn(component, 'onFilterChange').and.callThrough()
  spyOn(component.filterChange, 'emit')

  // initially the 3rd item in the dropdown is selected
  const INITIAL_FILTER_INDEX = 2
  // we want to select the 5th item in the dropdown
  const FILTER_INDEX = 4
  // the expected filter value is the value of the 5th dashboard filter (as used to populate the dropdown)
  const EXPECTED_FILTER_VALUE = getDashboardFiltersData.dashboardFilters[FILTER_INDEX].value

  // handle on the dropdown
  const filterDropdown = fixture.debugElement.query(By.css('select')).nativeElement

  // let bindings complete
  fixture.whenStable().then(() => {

    // ensure filterDropdown.value is stable
    expect(filterDropdown.value).toContain(getDashboardFiltersData.dashboardFilters[INITIAL_FILTER_INDEX].value)

    // update filterDropdown.value and dispatch change event
    filterDropdown.value = filterDropdown.options[FILTER_INDEX].value
    filterDropdown.dispatchEvent(new Event('change'))

    // check component data
    expect(component.dashboardFilterValue).toBe(EXPECTED_FILTER_VALUE)
    expect(component.dashboardFilterChangeInProgress).toBe(false)

    // check spies
    expect(component.onFilterChange).toHaveBeenCalledWith(EXPECTED_FILTER_VALUE)
    expect(setDashboardFilterSpy).toHaveBeenCalledWith(EXPECTED_FILTER_VALUE)
    expect(component.filterChange.emit).toHaveBeenCalledWith(true)
  })
})