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

Posted

技术标签:

【中文标题】如何对 ngControl 用 @Self 装饰的反应性组件进行单元测试【英文标题】:How to unit test a reactive component where ngControl is decorated with @Self 【发布时间】:2019-03-26 16:07:56 【问题描述】:

我通过注入 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 组件的方法。

【问题讨论】:

【参考方案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)
            
         ]
       
    );
 ));

【讨论】:

这是唯一有帮助的答案,因为我没有将 NgControl 注入我的构造函数,只是在文件中使用它?【参考方案2】:

尝试在 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 方法。 希望对你有帮助。

【讨论】:

以上是关于如何对 ngControl 用 @Self 装饰的反应性组件进行单元测试的主要内容,如果未能解决你的问题,请参考以下文章

如何在角度 2 中将输入值转换为大写(传递给 ngControl 的值)

如何使用给定的装饰器获取python类的所有方法

为啥我需要用 pyqtSlot 装饰连接的插槽?

Angular 2 - 使用带有可选字段的 ngControl

python 检查获取用@property 装饰的方法

用参数装饰类方法