30

I have a component A that use a component B,c,D in its template:

###template-compA.html

    <comp-b></comp-b>
    <comp-c [myinput]="obj.myinput"></comp-c>
    <comp-d ></comp-d>

...etc

To simplify, let's say their is only one directive in component A:

 ###template-compA.html

    <comp-b></comp-b>

My comp-b has its own dependencies (services or other comp).

If I want to test comp-a this way:

TestBed.configureTestingModule({
    declarations: [comp-A],
    imports: [ReactiveFormsModule],
}).overrideComponent(FAQListComponent, {
    set: {
    providers: [
        { provide: comp-AService, useValue: comp-AListSVC }
    ]
    }
})
    .compileComponents();

it would not work properly. So I do:

TestBed.configureTestingModule({
    declarations: [comp-A, comp-B],
    imports: [ReactiveFormsModule],
}).overrideComponent(FAQListComponent, {
    set: {
    providers: [
        { provide: comp-AService, useValue: comp-AListSVC }
    ]
    }
})
    .compileComponents();

It doesn't work also because comp-b doesn't have its own dependencies. And here I am confused, how can I do unit test if I have to import and remock all others components every single time ? It looks like a very big amount of work. Is there another way? What would be the best practice to test component with nested componentS that have their own dependencies ?

Thanks a lot,

Stéphane.

yankee
  • 38,872
  • 15
  • 103
  • 162
Stefdelec
  • 2,711
  • 3
  • 33
  • 40
  • 2
    Have you tried `schemas: [NO_ERRORS_SCHEMA]`? https://blog.nrwl.io/essential-angular-testing-192315f8be9b#.vygkcekn0 – yurzui Mar 22 '17 at 11:48

3 Answers3

24

If you don't need to reference comp-b in any way in your tests you can add schemas: [NO_ERRORS_SCHEMA] or [CUSTOM_ELEMENTS_SCHEMA] to your TestBed configuration or override the comp-A's template and remove the tag for comp-b

If you do need to reference comp-b you may not need to provide it's dependencies specifically in an override.

Setting providers in overrideComponent is only necessary if the dependency is provided in the component itself. (If you have a providers list in comp-A.ts)

let's say comp-b needs a comp-AService and comp-AService is being provided in your comp-A override, since comp-b is a child of comp-A it will have comp-AService provided for it.

If you are providing these dependencies in your app.module or somewhere higher than the component itself you don't need to override. For example if comp-b needs comp-AService & someOtherService which are both being provided in your app.module your TestBed configuration could look like this:

TestBed.configureTestingModule({
  declarations: [comp-A, comp-B],
  imports: [ReactiveFormsModule],
  providers: [
    { provide: comp-AService, useValue: comp-AListSVC },
    { provide: someOtherService, useValue: someOtherServiceSVC }
  ]
})

Edit:

You can read more about nested component testing here:

https://angular.io/guide/testing-components-scenarios#nested-component-tests

Fabian
  • 223
  • 4
  • 15
Borquaye
  • 756
  • 3
  • 9
  • 2
    What does the `NO_ERROR_SCHEMA` do? Does it mean that you want to ignore all children component: https://medium.com/@voorkanter/there-is-a-way-to-ingore-all-child-components-while-unit-testing-a-component-ceddeb56cc0c ? – skofgar May 19 '17 at 18:42
  • 2
    @skofgar the `NO_ERROR_SHEMA` tells the compiler to ignore unrecognised elements and attributes. you can read more about using it in tests here: https://angular.io/docs/ts/latest/testing/#!#shallow-component-test I've updated the answer to include this link – Borquaye May 19 '17 at 22:58
  • Thanks for adding the info! – skofgar May 22 '17 at 17:43
  • Sure, no worries :) – Borquaye May 22 '17 at 19:50
4

Following a @yurzui 's advice:

beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [comp-a],
      schemas: [NO_ERRORS_SCHEMA]
    })
      .compileComponents();
  }));
Stefdelec
  • 2,711
  • 3
  • 33
  • 40
1

It's an old question but this is what I'm currently doing in Angular 9.

As you'd want to isolate your tested component as much as possible, you can create a function that returns a fake component to import, instead of the actual nested component:

  function mockComponent(selector: string) {
    @Component({
      selector,
      template: ''
    })
    class MockValueAccessorComponent {
      // [mock implementation here]
    }

    return MockValueAccessorComponent;
  }

And then import it into your TestBed like this:

  TestBed.configureTestingModule({
    declarations: [
      TestUtils.mockComponent('component-template-to-mock'),
    ]
  });
Raschid JFR
  • 580
  • 8
  • 15