问题
I've written a reactive component by injecting the NgControl and which is decorated using the @Self decorator. My problem is related to unit testing of such component. Please look at the code below:
Disclaimer: I've quickly copied the code and made some inline changes. So, this might not be a compiler happy code.
My Reactive Component:
@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
}
Unit Test:
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')
});
}
Whenever I try to run the above unit test. It fails with the following error:
Template parse errors: No provider for NgControl --> <text-input>....</text-input>
So I'm looking for a way to successfully run the above unit test. What, I'm looking for is a way to inject the NgControl to the TextInput
component.
回答1:
If anybody stumbles upon this question, I solved it using the overrideComponent() method of the TestBed class.
Note: If you think you have some other answers, please feel free to answer this.
To inject the NgControl:
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [
TextInput, TextInputHost
],
imports: [
FormsModule, ReactiveFormsModule
]
})
.overrideComponent(TextInput, {
set: {
providers: [
{
provide: NgControl,
useValue: new FormControlDirective([], [], null, null)
}
]
}
});
}));
回答2:
Try to add decorator @Optional before @Self in your constructor for controlDir property.
@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
}
and you can remove overrideComponent method from TestBed in your tests.
I hope it helps.
来源:https://stackoverflow.com/questions/52931778/how-to-unit-test-a-reactive-component-where-ngcontrol-is-decorated-with-self