Angular 8 单元测试,无法设置 属性 'valueAccessor' 为 null

Angular 8 Unit Test, Cannot set property 'valueAccessor' of null

我试图简单地通过测试 (应该创建 - component=> toBeTruthy) 用于自定义组件(angular 组件 class) 使用 ControlValueAccessor 实现。我在 constructor 中注入了 NgControl 并传递了 this 关键字。

 constructor(@Optional() @Self() public controlDir?: NgControl) {
    this.controlDir.valueAccessor = this;
  } 

然后在 ngOnInit()

ngOnInit() {
    const control = this.controlDir.control;
    const validators = control.validator ? [control.validator] : [];
    const asyncValidators = control.asyncValidator ? [control.asyncValidator] : [];

    control.setValidators(validators);
    control.setAsyncValidators(asyncValidators);
    control.updateValueAndValidity();
  }

在测试文件中

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

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [TextInputComponent],
      imports: [ReactiveFormsModule]
    })
      .compileComponents();
  }));

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

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

});

以及错误

TypeError: Cannot set property 'valueAccessor' of null

如果有人分享您解决此问题的想法,我将不胜感激?

谢谢。 拉宾

您可以像这样创建一个模拟 class NgControl :

export class TestNgControl {
 valueAccessor : any = null;
} 

然后在providers for configureTestingModule方法中,你可以这样写:

beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [TextInputComponent],
      imports: [ReactiveFormsModule],
      providers : [{ provide : NgControl, useClass : TestNgControl}]
    })
      .compileComponents();
  }));

请告诉我这是否有效。

我通过调用包含表单的 <app-text-input formControlName="test"></app-text-input> 模板创建新的模拟 class 组件解决了这个问题。

这里是代码详情:

@Component({
  template: `
  <form [formGroup]="form">
   <app-text-input formControlName="input">
   </app-text-input>
  </form>`
})
class TestingInput {
  @ViewChild(TextInputComponent, { static: true })
  textInputComponent: TextInputComponent;

  form = new FormGroup({
    input: new FormControl('Test Value')
  });
}

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

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [TextInputComponent, TestingInput],
      imports: [ReactiveFormsModule]
    })
      .compileComponents();
  }));

  beforeEach(() => {
    fixture = TestBed.createComponent(TestingInput);
    component = fixture.componentInstance.textInputComponent;
  });

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

谢谢大家的回答和评论,都没有解决确切的问题,但至少有想法弄清楚。 如果您有任何其他更好的方法来解决此问题,请随时post回答。 非常感谢!

我通过为 controlDir 添加 @Optional 装饰器解决了这个问题,然后在 if 语句中将其赋值给 controlDir.valueAccessor,因为 controlDir 不会在测试中赋值。

  constructor(@Self() @Optional() public controlDir: NgControl) {
    if (controlDir) {
      controlDir.valueAccessor = this;
    }
  }

然后在您的 ngOnInit() 中,您还需要将任何访问 controlDir 的调用包装在 if 语句中,因为它不会被设置。

  ngOnInit() {
    if (this.controlDir) {
      const control = this.controlDir.control;
      const validators = control.validator
        ? [control.validator, this.validate]
        : this.validate;
      control.setValidators(validators);
      control.updateValueAndValidity();
    }
  }

有些人在使用上述内容构建产品时遇到问题,并且还使用下面 URL 中的说明解决了您的问题,该说明在测试中使用了 TestbedStatic.overrideComponent。

https://github.com/angular/angular/issues/31632