No Value Accessor Error in Angular Tests
I was recently tasked with troubleshooting failing Angular unit tests for a component in a large codebase I was completely unfamiliar with. The tests were failing with:
Error: No value accessor for form control with unspecified name attribute
The call stack wasn't helpful at all. It included only Angular internals. The component worked just fine in the application.
My first guess was a missing import for the FormsModule
in the test. But it was there:
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [FormComponent],
imports: [FormsModule],
}).compileComponents();
});
I could only resolve the issue after carefully inspecting the component template and learning about all the components that were used in it. Among them was ng-select which required its NgSelectModule
module to be imported (as documented). The module was already imported in AppModule
because the component had been used elsewhere in the application. Adding the module to the test indeed helped:
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [FormComponent],
imports: [FormsModule, NgSelectModule],
}).compileComponents();
});
To learn more about the issue so that I could troubleshoot similar ones more effectively in the future, I tried to make a minimal reproduction. An almost trivial component using ng-select was enough for that:
<ng-select [items]="items" [(ngModel)]="selectedItem"></ng-select>
<p>Selected: {{ selectedItem }}</p>
If the NgSelectModule
wasn't imported in the test setup, even the default auto-generated 'should create'
tets failed with the same error:
beforeEach(() => {
fixture = TestBed.createComponent(FormComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it("should create", () => {
expect(component).toBeTruthy();
});
Having only a single test in the suite helped notice another error that was listed in the console output (both in browser and at command line) but not in the Karma results page:
ERROR: 'Can't bind to 'items' since it isn't a known property of 'ng-select'.'
Seeing this when I was originally troubleshooting the issue would put me on the right track sooner. Definitely something to check although the output would be more difficult to notice in a larger codebase with more console output of its own.
Interestingly enough, removing the FormsModule
from the test made it pass but resulted in an even more descriptive error in the console:
ERROR: ''ng-select' is not a known element:
1. If 'ng-select' is an Angular component, then verify that it is part of this module.
2. If 'ng-select' is a Web Component then add 'CUSTOM_ELEMENTS_SCHEMA' to the '@NgModule.schemas' of this component to suppress this message.'
I'm not sure how useful that would be in a real-world troubleshooting scenario. Still, it's something to keep in mind as another tool in the toolbox when trying to gather as much information as possible.
You can find the code for a minimal reproduction of the issue in my GitHub repository if you want to experiment with it yourself.
Although I already knew that the tests must import the same modules as the application code, it's not always easy to determine which exactly these are in a large unknown codebase. When the error for a failing test in the Karma results page isn't all that helpful, there might be more errors in the browser console. The log level filter in the browser developer tools can be used to look at those even when the code under test does "too much" console logging.