简体   繁体   English

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

[英]Unit testing component with @Input / @Output and reactive form

This is my component: 这是我的组件:

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();
  }

}

The template: 模板:

<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>

The test 考试

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'] });
  });
});

The test fails because 测试失败,因为

a) 一种)

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

Does not lead to the form having two option elements 不导致表单具有两个选项元素

and b) 和b)

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

Is called, but with { vals: null } 被调用,但使用{ vals: null }

The code works, the app itself works fine, just the test is failing. 代码工作正常,应用程序本身正常运行,只是测试失败。

How do I set up the form properly, the @Input element? 如何正确设置表单@Input元素?

I have looked at some blog posts but have not been able to adapt them to my code. 我看过一些博客文章,但无法使其适应我的代码。

This is because you are using Onpush strategy. 这是因为您使用的是Onpush策略。 When onPush is used change detection is propagating down from the parent component not the component itself. 当使用onPush时,更改检测从父组件而不是组件本身向下传播。

My suggestion is to wrap your test component inside a host component. 我的建议是将测试组件包装在主机组件中。 There is a pending issue regarding this on Angular's gitHub page you can look it up for further reading. Angular的gitHub页面上存在与此相关的未决问题,您可以查找它以进一步阅读。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM