如何对使用@Self装饰ngControl的反应组件进行单元测试

时间:2018-10-22 14:30:07

标签: angular unit-testing angular-reactive-forms angular-components

我已经通过注入NgControl编写了一个反应组件,并使用@Self装饰器对其进行了装饰。我的问题与此类组件的单元测试有关。 请查看下面的代码:

免责声明:我已经快速复制了代码并进行了一些内联更改。因此,这可能不是编译器满意的代码。

我的反应组件:

@Component({
  selector: 'text-input',
  templateUrl: '<input type="text" class="native_input" />'
})
class TextInput implements ControlValueAccessor {
  protected constructor(@Self() public controlDir: NgControl) {

    this.controlDir.valueAccessor = this;
  }

  // ...followed by other ControlValueAccessor methods

}

单元测试:

describe('TextInput -', () => {

   let fixture: ComponentFixture<TextInputHost>;
   let textInput: TextInput;

   let inputElement: HTMLInputElement;

   beforeEach(async(() => {
     TestBed.configureTestingModule({
       declarations: [
         TextInput, TextInputHost
       ],
       imports: [
         FormsModule, ReactiveFormsModule
       ]
     });
  }));

  beforeEach(fakeAsync(() => {
    fixture = getTestBed().createComponent(TextInputHost);
    textInput = fixture.componentInstance.textInputComponent;
    textInput.writeValue('TestValue');
    inputElement = fixture.debugElement
       .query(By.css('native_input')).nativeElement;
    fixture.detectChanges();
    tick();
  }));

  it('Should have the initial value applied.', () => {
    expect(inputElement.value).toBe('TestValue');
  });

});

// Host component
@Component({
  template: `
   <form [formGroup]="pageForm">
    <text-input formControlName="testInput">
    </text-input>
   </form>`
})
class TextInputHost {
   @ViewChild(TextInput)
   public textInputComponent: TextInput;

   public pageForm: FormGroup = new FormGroup({
     testInput: new FormControl('Initial Value')
   });
}

每当我尝试运行上述单元测试时。失败并显示以下错误: Template parse errors: No provider for NgControl --> <text-input>....</text-input>

因此,我正在寻找一种成功运行上述单元测试的方法。我正在寻找的是一种将NgControl注入到TextInput组件中的方法。

2 个答案:

答案 0 :(得分:1)

如果有人偶然发现了这个问题,我可以使用TestBed类的overrideComponent()方法解决它。

注意:如果您认为还有其他答案,请随时回答。

要注入NgControl:

beforeEach(async(() => {
     TestBed.configureTestingModule({
       declarations: [
         TextInput, TextInputHost
       ],
       imports: [
         FormsModule, ReactiveFormsModule
       ]
    })
    .overrideComponent(TextInput, {
        set: {
          providers: [
            {
              provide: NgControl,
              useValue: new FormControlDirective([], [], null, null)
            }
         ]
       }
    });
 }));

答案 1 :(得分:0)

尝试在构造函数中的controlDir属性的@Self之前添加装饰器@Optional。

@Component({
  selector: 'text-input',
  templateUrl: '<input type="text" class="native_input" />'
})
class TextInput implements ControlValueAccessor {
  protected constructor(
     @Optional() // <- in this place
     @Self() public controlDir: NgControl) {

    this.controlDir.valueAccessor = this;
  }

  // ...followed by other ControlValueAccessor methods

}

,您可以在测试中从TestBed中删除overrideComponent方法。
希望对您有所帮助。