2

I am attempting to independently test a child component that requires a FormGroup as input, as the child represents a section on the parent form. We use an in-house framework that relies on an entity / entity service hierarchy to automagically build the forms for us, which is very nice, as these are large forms and would be incredibly tedious to do by hand. So I need to actually inject the parent entity service in order to build the form I need to test the child component, and thus far I have been unable to make it work. The below is a sample of what I'm trying to do:

beforeEach(() => {
  return MockBuilder(ChildComponent, [
    SharedModule,
    LazyLoadedModule
   ])
    .keep(ParentEntityService);
});

it('should create', () => {
  const fixture = MockRender(ChildComponent);
  const service = fixture.point.injector.get(ParentEntityService);
  const formGroup = service.createForm(...);
  fixture.componentInstance.formGroup = formGroup;
  fixture.detectChanges();
  expect(fixture.point.componentInstance).toBeTruthy();
});

The main problem I'm having is that as soon as MockRender is called, console.log() just stops working, so I can't see the value of fixture or anything I'm trying to do - I'm not sure why.

If anyone can help I'd appreciate it.

EDIT: I've added a StackBlitz where I've replicated the issue as best I could without revealing closed source code. https://stackblitz.com/edit/ng-mocks-sandbox-crrf85

As you can see, the test blows up when the ngOnInit runs as the FormGroup passed to the child component is undefined. The issue is that the service should be building the form. With regular angular tests and TestBed.inject before calling the service, it works fine, but I want to use ng-mocks so I can mock everything but whatever I'm testing and the service.

Xyn
  • 41
  • 3
  • `MockRender` detects changes by default, you need to disable it: https://ng-mocks.sudo.eu/api/MockRender#fixturedetectchanges – satanTime Jul 28 '22 at 05:51
  • Also, could you share the source code of `ChildComponent` and `ParentEntityService`? – satanTime Jul 28 '22 at 06:14
  • The source code is proprietary, so I can't share it. The important thing is that createForm functionality - if given nothing - will create an empty form. If it's given a TS object, it will create a form with the form controls having the correspondingly-named values from the TS object filled in. All I need to do is be able to create the form so I can set it as an input on the ChildComponent. If I use regular Angular unit testing setup with TestBed.inject, it works fine... but I want to utilize ng-mocks so we can mock everything that doesn't actually matter. – Xyn Jul 28 '22 at 14:20
  • I tried disabling the automatic call to detect changes, and I got an error that the ServiceInjector needed the injector set, so I added `ServiceInjector.setInjector(fixture.point.injector);` but that made no difference. – Xyn Jul 28 '22 at 14:38
  • I see, unfortunately, without having a min example with the issue it's hard to help you. I would suggest to try `MockInstance` or `MockRenderFactory`, both of them allow to modify services before rendering. – satanTime Aug 06 '22 at 08:09
  • I added a StackBlitz where I replicated the issue as best I could. Getting these tests fixed has been put on the back burner for the moment so I have other fires to put out, or I would try playing with MockRenderFactory etc. – Xyn Aug 15 '22 at 14:36

1 Answers1

0

As discussed in gitter and based on the min repo you provided.

if you cannot mock the forms generation, you should .keep all dependencies which help to build them.

There is a passing example of your test: https://stackblitz.com/edit/ng-mocks-sandbox-8amzcu?file=src%2Ftests%2Fservice-create-form%2Ftest.spec.ts

What I changed:

  • added .keep(ReactiveFormsModule)
  • added .keep(FormBuilder)
  • disabled change detection on MockRender(ChildComponent, null, false);, because render depends on the correct value of fixture.componentInstance.formGroup.

I can see the test is green now.

satanTime
  • 12,631
  • 1
  • 25
  • 73
  • I tried this in the code at work and I think it will make the test pass if I can get rid of the null injection errors about Router etc. I'm not sure what all their modules are doing - I haven't really looked at the code, I just consume their libraries. – Xyn Aug 23 '22 at 14:12
  • Try to add Router to the second parameter of `MockBuilder`, or even the whole `RouterModule.forRoot([])`. – satanTime Sep 25 '22 at 10:37