How to mock ControlContainer in angular unit test?
Asked Answered
S

2

18

How can I mock a ControlContainer instance so that I can test my component?

I have a child component that injects a ControlContainer into the constructor, so its usage is

<acr-score-card formGroupName="score"></acr-score-card>

and the component itself is

@Component({
  selector: 'acr-score-card',
  templateUrl: './score-card.component.html',
  styleUrls: ['./score-card.component.scss']
})
export class ScoreCardComponent implements OnInit {

  ...

  form: FormGroup;

  constructor(private ngControl: ControlContainer) { }

  ngOnInit() {
    this.form = <FormGroup>this.ngControl.control;
  }

  ...

}

Everything works fine when I run in the browser but I cannot get the unit tests to work as I am not sure how to mock the ControlContainer instance in order to setup the provider, this is the contents of my spec file:

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

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      imports: [TestingModule],
      declarations: [ScoreCardComponent],
      providers: [/** what goes here to mock the ControlContainer */]
      schemas: [NO_ERRORS_SCHEMA]
    })
      .compileComponents();
  }));

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

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

So, to repeat the question, how can I mock a ControlContainer instance so that I can test my component?

Sabulous answered 10/1, 2019 at 9:48 Comment(1)
Can you check this link? It shows a way inject COntrolContainers. But since ControlContainer is an interface, you need create instance with the classes that implement the interface like FormGroupDirective etc.Entire
S
38

Thanks to @KiraAG for comment and was able to work out what was required from provided link, so posting answer here in case anyone else comes across this question

So in your test you need to provide the ControlContainer instance in your test module, this is basically going to be either a FormGroupDirective or a FormControlDirective depending on what you expect to be passed to your component.

For example, in your test file create the FormGroup that represents the part of the form you are using

const fg: FormGroup = new FormGroup({
  'answer': new FormControl(''),
  ...
});

Next create a FormGroupDirective and set the form property to the FormGroup created above

const fgd: FormGroupDirective = new FormGroupDirective([], []);
fgd.form = fg;

Now in you test module setup you can provide the ControlContainer

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      imports: [TestingModule],
      declarations: [ScoreCardComponent],
      providers: [
        { provide: ControlContainer, useValue: fgd }
      ]
      schemas: [NO_ERRORS_SCHEMA]
    })
      .compileComponents();
  }));

And that's it, the constructor injection is satisfied.

Sabulous answered 14/1, 2019 at 15:21 Comment(5)
I am followed this, but getting Error: No provider for FormGroupDirective!Anticlinal
I get the following error: 'Cannot find control with unspecified name attribute any' thoughts?Candis
I inherited this code ... this.scheduleFormGroup = this.controlContainer.control.get('schedule'); ... so I had to wrap it like this ... const schedule: FormGroup = new FormGroup({ schedule: new FormGroup({ minute: new FormControl('/5'), hour: new FormControl(''), date: new FormControl(''), month: new FormControl(''), day: new FormControl('*'), active: new FormControl(false) }) });Alleged
@RebeccaDouglas I had the same problem initially. In my components I have an input [controlName] that is used to get the control from the ControlContainer that is injected. When I passed that fixture.component.controlName = 'theName' my spec worked.Ylem
BTW: I was googling for this solution several times unsuccesfully, and now I realize this was here all the time since early 2019. But finally the needed solution is found. Thx allot @neil-stevens 🙏Ylem
J
6

I used Neil Stevens's answer.

But I had

viewProviders: [{provide: ControlContainer, useExisting: FormGroupDirective}],

in the component.

So in tests I have to use

providers: [{provide: FormGroupDirective, useValue: fgd}],

Maybe this will help someone.

Johathan answered 25/2, 2020 at 11:41 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.