How to set Angular viewProviders with an NgForm in a unit test?

170 Views Asked by At

I have an Angular 16 component that gets passed the form that it will be inside of, and the viewProviders allows that to work which I received as an answer to another question I asked

@Component({
  selector: 'app-question-form-editor',
  standalone: true,
  imports: [ CommonModule, FormsModule ],
  templateUrl: './question-form-editor.component.html',
  viewProviders: [{ provide: ControlContainer, useExisting: NgForm }],
})
export class QuestionFormEditorComponent implements OnInit {
  @Input() form!: NgForm;
  
  //Logic goes here...
}

This now works as expected, however now my unit tests break since they need to have this provider and I'm not sure what value I need to provide.

const mockQuestionForm = new NgForm([], []);
mockQuestionForm.form.addControl('example', new FormControl('', Validators.required));

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

  beforeEach(() => {
    TestBed.configureTestingModule({
      imports: [QuestionFormEditorComponent],
      providers: [{ provide: ControlContainer, useValue: mockQuestionForm }],
    });
    fixture = TestBed.createComponent(QuestionFormEditorComponent);
    component = fixture.componentInstance;

    component.form = mockQuestionForm;
  });

  it('should create', () => {
    fixture.detectChanges(); //triggers ngOnInit()

    expect(component).toBeTruthy();
  });
});

I have also tried useExisting instead of useValue and have tried NgForm instead of my mockQuestionForm - all have the same result

Here is the error message Jasmine fails with

NullInjectorError: R3InjectorError(Standalone[QuestionFormEditorComponent])[NgForm -> NgForm -> NgForm]: 
  NullInjectorError: No provider for NgForm!
error properties: Object({ ngTempTokenPath: null, ngTokenPath: [ 'NgForm', 'NgForm', 'NgForm' ] })
    at NullInjector.get (http://localhost:9876/_karma_webpack_/webpack:/node_modules/@angular/core/fesm2022/core.mjs:8890:27)
    at R3Injector.get (http://localhost:9876/_karma_webpack_/webpack:/node_modules/@angular/core/fesm2022/core.mjs:9334:33)
    at R3Injector.get (http://localhost:9876/_karma_webpack_/webpack:/node_modules/@angular/core/fesm2022/core.mjs:9334:33)
    at R3Injector.get (http://localhost:9876/_karma_webpack_/webpack:/node_modules/@angular/core/fesm2022/core.mjs:9334:33)
    at ChainedInjector.get (http://localhost:9876/_karma_webpack_/webpack:/node_modules/@angular/core/fesm2022/core.mjs:14018:36)
    at lookupTokenUsingModuleInjector (http://localhost:9876/_karma_webpack_/webpack:/node_modules/@angular/core/fesm2022/core.mjs:4608:39)
    at getOrCreateInjectable (http://localhost:9876/_karma_webpack_/webpack:/node_modules/@angular/core/fesm2022/core.mjs:4656:12)
    at ɵɵdirectiveInject (http://localhost:9876/_karma_webpack_/webpack:/node_modules/@angular/core/fesm2022/core.mjs:11801:19)
    at ɵɵinject (http://localhost:9876/_karma_webpack_/webpack:/node_modules/@angular/core/fesm2022/core.mjs:848:60)
    at NodeInjectorFactory.factory (http://localhost:9876/_karma_webpack_/webpack:/node_modules/@angular/core/fesm2022/core.mjs:9556:29)
1

There are 1 best solutions below

0
On BEST ANSWER

You should be able to override component annotation settings as follows:

TestBed.overrideComponent(
  MyComponent,
  {
    set: {
      viewProviders: [{
//         your view provider code goes here
      }]
    }
  }
);

(Code example comes from here)

In your case, I think you are missing the FormsModule and/or ReactiveFormsModule... I would suggest to try importing those into your test module and see if that resolves your issue before taking this customization step.