Angular: Mock Component Transclusion in Angular 7+

in code


Take tabbed content, where, at any time only one pane is shown out of several given. This is such a common pattern that the Angular guide for @ContentChildren uses tabs as an example of use. In very short, @ContentChildren() is syntactic sugar for the results of a query the compiled DOM. Matching elements are available for manipulation a QueryList object.

@ContentChildren(TabPaneComponent) tabs: QueryList<TabPaneComponent>;

There are a million guides about@ContentChildren which I won’t rehash here. Instead I’ll briefly talk about how you mock (e.g.) a tab and its panes for testing. A bunch of searches for “mock contentchildren” or “mock angular transclusion” lead me nowhere. It was when I looked at treating the QueryList as a programmatic entity that better results jumped out at me. The essence of mocking is simple: create a wrapper component whose template includes tabbed content, and then query for the components you want.

Existing Stack Overflow posts such as this are mostly correct, though their accepted solutions use outdated syntax.

@Component({
  selector: 'lib-tab-test',
  template: `
    <lib-tab>
      <lib-tab-pane></lib-tab-pane>
      <lib-tab-pane></lib-tab-pane>
      <lib-tab-pane></lib-tab-pane>
    </lib-tab>
  `
})
class TabTestComponent {}

Querying and testing thereafter is slightly different, in that you extract TabComponent from a TabTestComponent fixture:

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

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [
        TabComponent,
        TabPaneComponent,
        TabTestComponent
      ],
      imports: [
        // ...
      ]
    })
    .compileComponents();
  }));

  beforeEach(() => {
    fixture = TestBed.createComponent(TabTestComponent);

    component = fixture.debugElement
      .query(By.directive(TabComponent))
      .injector.get(TabComponent);
  });

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

Using By.directive as seen here is a neater way to query for TabCompnent than fixture.debugElement.children[0].componentInstance;

And that’s it! All Angular content transclusion can be tested in this manner.




Your email address will not be published. Required fields are marked *