具有@Input / @Output和反应形式的单元测试组件

时间:2018-08-24 08:24:46

标签: javascript angular testing ngrx

这是我的组件:

import { Component, OnInit, Input, Output, EventEmitter, ChangeDetectionStrategy, OnDestroy } from '@angular/core';
import { FormBuilder, FormGroup , Validators } from '@angular/forms';
import { Subscription } from 'rxjs';
import { Values } from '../_models/values';

@Component({
  selector: 'some',
  templateUrl: './my.component.html',
  styleUrls: ['./my.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class Mycomponent implements OnInit, OnDestroy {

  @Input()
  values: Array<string>;

  @Output()
  selectedValues = new EventEmitter<Values>();

  private myForm: FormGroup;

  @Input()
  errorMsg: string;

  private selectSubscription: Subscription;

  constructor(private fb: FormBuilder) {}

  ngOnInit() {
    this.myForm = this.fb.group({
      'selectAll': [false],
      'values': [this.values, Validators.required]
    });

    this.selectSubscription = this.myForm.get('selectAll').valueChanges.subscribe(value => {
      this.changeSelection(value);
    });
  }

  submit(): void {
    console.log('called');

    console.log(this.myForm.value.values);

    const theSelectedValues = {
      vals: this.myForm.value.values
    };
    this.selectedValues.emit(theSelectedValues);
  }

  private changeSelection(selectAll: boolean): void {
    if (selectAll) {
      const valuesSelect = this.myForm.controls['values'];
      valuesSelect.disable();
    } else {
      this.myForm.controls['values'].enable();

    }
  }

  ngOnDestroy() {
    this.selectSubscription.unsubscribe();
  }

}

模板:

<form [formGroup]="myForm" (ngSubmit)="submit()">
  <fieldset>
    <mat-checkbox formControlName="all">Select all</mat-checkbox>
  </fieldset>
  <fieldset>
    <select id="chooser" formControlName="values" multiple>
      <option *ngFor="let val of values?.urls" [value]="val">{{val}}</option>
    </select>
  </fieldset>
  <button mat-button [disabled]="myForm.invalid">Go!</button>
</form>
<div *ngIf="errorMsg">{{errorMsg}}</div>

测试

import { async, ComponentFixture, TestBed } from '@angular/core/testing';

import { Mycomponent } from './my.component';
import { By } from '@angular/platform-browser';
import { FormBuilder } from '@angular/forms';
import { NO_ERRORS_SCHEMA } from '@angular/core';

describe('Mycomponent', () => {
  let component: Mycomponent;
  let fixture: ComponentFixture<Mycomponent>;


  beforeEach(async(() => {
    TestBed.configureTestingModule({
      imports: [],
      schemas: [NO_ERRORS_SCHEMA],
      declarations: [Mycomponent],
      providers: [
        FormBuilder      ]
    })
      .compileComponents();

  }));

  beforeEach(() => {
    fixture = TestBed.createComponent(Mycomponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

  it('should create', () => {
    expect(component).toBeTruthy();
  });

  it('should emit selected values', () => {

    spyOn(component.selectedValues, 'emit');
    component.values = ['abc', 'de'];

    fixture.detectChanges();

    expect(fixture.debugElement.queryAll(By.css('option')).length).toBe(2); // is 0

    const formDE = fixture.debugElement.query(By.css('form'));
    formDE.triggerEventHandler('ngSubmit', {});

    // urls: null
    expect(component.selectedValues.emit).toHaveBeenCalledWith({ vals: ['abc', 'de'] });
  });
});

测试失败是因为

a)

component.values = ['abc', 'de'];

不会导致表单具有两个选项元素

和b)

expect(component.selectedValues.emit).toHaveBeenCalledWith({ vals: ['abc', 'de'] });

被呼叫,但是被{ vals: null }

代码有效,应用本身运行正常,只是测试失败。

@Input元素如何正确设置表单?

我看过一些博客文章,但无法使其适应我的代码。

1 个答案:

答案 0 :(得分:1)

这是因为您正在使用Onpush策略。使用onPush时,更改检测从父组件而不是组件本身向下传播。

我的建议是将测试组件包装在主机组件中。 Angular的gitHub页面上有一个悬而未决的问题,您可以查询一下以进一步阅读。