使用Jasmine测试时如何模拟MatChipInput

我已经设置了stackblitz并基本显示了问题所在。

基本上,当我尝试在包含MatChipList的MatFormField上触发事件时,出现

错误
 Cannot read property 'stateChanges' of undefined at MatChipInput._onInput

我尝试用MatInput的替代模拟覆盖MatChip模块。我还尝试覆盖该指令。

HTML

 <h1>Welcome to app!!</h1>

 <div>
  <mat-form-field>
   <mat-chip-list #chipList>
    <mat-chip *ngFor="let contrib of contributors; let idx=index;" [removable]="removable" (removed)="removeContributor(idx)">
     {{contrib.fullName}}
    </mat-chip>
    <input  id="contributor-input"
        placeholder="contributor-input"
        #contributorInput
        [formControl]="contributorCtrl"
        [matAutocomplete]="auto"
        [matChipInputFor]="chipList"
        [matChipInputSeparatorKeyCodes]="separatorKeysCodes"
        [matChipInputAddOnBlur]="addOnBlur">
  </mat-chip-list>
 </mat-form-field>
</div>

TS

import { Component,Input } from '@angular/core';
import { COMMA,ENTER } from '@angular/cdk/keycodes';
import { FormControl } from '@angular/forms';
import { Observable } from 'rxjs';
import { map,startWith } from 'rxjs/operators';

@Component({
  selector: 'my-app',templateUrl: './app.component.html',styleUrls: [ './app.component.css' ]
})
export class AppComponent  {

contributors = [{fullName: 'foo bar'}];
removable = true;
addOnBlur = false;
separatorKeysCodes: number[] = [
    ENTER,COMMA,];

contributorCtrl = new FormControl();

filteredPeople: Observable<Array<any>>;

@Input() peopleArr = [];

constructor() {
   this.filteredPeople = this.contributorCtrl.valueChanges.pipe(startWith(''),map((value: any) => 
this.searchPeople(value)));
 }

 searchPeople(searchString: string) {
    const filterValue = String(searchString).toLowerCase();
    const result = this.peopleArr.filter((option) => option.fullName.toLowerCase().includes(filterValue));
    return result;
  }
}

SPEC

import { RouterTestingModule } from '@angular/router/testing';
import { AppComponent } from './app.component';
import { TestBed,async,ComponentFixture } from '@angular/core/testing';
import { 
  BrowserDynamicTestingModule,platformBrowserDynamicTesting 
} from '@angular/platform-browser-dynamic/testing';
import {NoopAnimationsModule} from '@angular/platform-browser/animations';
import {  MatFormFieldmodule,MatAutocompleteModule,MatInputModule,MatChipsModule } from '@angular/material';
import { FormsModule,ReactiveFormsModule} from '@angular/forms';

describe('AppComponent',() => {

  const mockPeopleArray = [
    { personId: 1,email: 'foo1@bar.com',department: 'fake1',username: 'foo1',fullName: 'Foo Johnson'
     },{ personId: 2,email: 'foo2@bar.com',username: 'foo2',fullName: 'John Fooson'
     },{ personId: 3,department: 'fake2',username: 'foo3',fullName: 'Mary Smith'
     }
 ];


 let app: AppComponent;
 let fixture: ComponentFixture<AppComponent>;
 let nativeElement: HTMLElement;

 beforeAll( ()=> {
  TestBed.initTestEnvironment(BrowserDynamicTestingModule,platformBrowserDynamicTesting());
  });
  beforeEach(
   async(() => {
     TestBed.configureTestingModule({
       imports: [
       RouterTestingModule,MatFormFieldmodule,FormsModule,ReactiveFormsModule,MatChipsModule,NoopAnimationsModule
       ],declarations: [AppComponent]
     }).compileComponents();

   fixture = TestBed.createComponent(AppComponent);
   app = fixture.debugElement.componentInstance;
   nativeElement = fixture.nativeElement;
  })
 );
 it(
 'should render title \'Welcome to app!!\' in a h1 tag',async(() => {
  fixture.detectChanges();
  expect(nativeElement.querySelector('h1').textContent).toContain('Welcome to app!!');
})
);

it('searchPeople should trigger and filter',(done) => {
  app.peopleArr = mockPeopleArray;

  const expected = [
    { personId: 3,fullName: 'Mary Smith'
     }
  ];

  const myInput = <HTMLInputElement> 
  nativeElement.querySelector('#contributor-input');
  expect(myInput).not.toBeNull();
  myInput.value = 'Mar';
  spyOn(app,'searchPeople').and.callThrough();
  myInput.dispatchEvent(new Event('input'));
    fixture.detectChanges();
    fixture.whenStable().then(() => {
        const myDiv = nativeElement.querySelector('#contrib-div');
        expect(app.searchPeople).toHaveBeenCalledWith('mar');
        app.filteredPeople.subscribe(result => 
        expect(result).toEqual(<any>expected));
        done();
    });
  });
 });
qq090630 回答:使用Jasmine测试时如何模拟MatChipInput

您将得到:

  

无法读取位于的未定义属性'stateChanges'   MatChipInput._onInput

由于触发时Angular尚未完成绑定myInput.dispatchEvent(new Event('input'))

要解决此问题,应首先调用sos-papermill,以便Angular执行数据绑定。

然后,由于所有操作都是同步执行的,因此您无需使该测试成为非常规的。

现在考虑您的searchPeople方法。自您使用startWith('')开始使用初始值进行订阅以来,它将被调用两次:

this.contributorCtrl.valueChanges.pipe(startWith('')

因此,您需要跳过第一个通话,并在触发input事件后检查通话结果。

app.filteredPeople.pipe(skip(1)).subscribe(result => {
  ...
});

spyOn(app,"searchPeople").and.callThrough();

myInput.dispatchEvent(new Event("input"));
expect(app.searchPeople).toHaveBeenCalledWith("Mar");

测试的整个代码:

it("searchPeople should trigger and filter",() => {
  app.peopleArr = mockPeopleArray;

  const expected = [
    {
      personId: 3,email: "foo1@bar.com",department: "fake2",username: "foo3",fullName: "Mary Smith"
    }
  ];

  fixture.detectChanges();
  const myInput = nativeElement.querySelector<HTMLInputElement>(
    "#contributor-input"
  );
  expect(myInput).not.toBeNull();
  myInput.value = "Mar";

  app.filteredPeople.pipe(skip(1)).subscribe(result => 
    expect(result).toEqual(expected);
  );

  spyOn(app,"searchPeople").and.callThrough();

  myInput.dispatchEvent(new Event("input"));
  expect(app.searchPeople).toHaveBeenCalledWith("Mar");
}); 

fixture.detectChanges

本文链接:https://www.f2er.com/3136569.html

大家都在问