Steve Van Opstal
Steve Van Opstal

Reputation: 1121

Angular 2.0.0-rc.1: How to test elements on the view or content (e.g. @ViewChildren, @ContentChildren)

I have a component that gets data from a service which creates children on the View. Those children are only available when the View is created. In my example below the View is not created before it reaches its tests, thus test 2 fails.

component:

@Component({
  selector: 'component-to-test',
  providers: [Service],
  directives: [NgFor, ChildComponent],
  template: `
    <child [data]="childData" *ngFor="let childData of data"></child>
})

export class ComponentToTest implements OnInit {
  @ViewChildren(ChildComponent) children: QueryList<ChildComponent>;
  private data: any;

  public ngOnInit() {
    this.getData();
  }

  private getData() {
    // async fetch data from a service
  }
}

spec:

describe('ComponentToTest', () => {
  beforeEach(inject([ComponentToTest], (component: ComponentToTest) => {
    component.ngOnInit();
  }));


  it('should initialise data', inject([ComponentToTest], (component: ComponentToTest, service: Service) => {
    return service.getData().toPromise().then(() => {
      expect(component.data).toBeDefined();
    })
  }));

  it('should initialise children', inject([ComponentToTest], (component: ComponentToTest, service: Service) => {
    return service.getData().toPromise().then(() => {
      expect(component.children).toBeDefined();
    })
  }));
});

Test 1 passes, test 2 fails. How do you test something that is created after the View or Content is initialised?

Upvotes: 2

Views: 1860

Answers (2)

G&#252;nter Z&#246;chbauer
G&#252;nter Z&#246;chbauer

Reputation: 657937

Injecting a component only creates an instance of the component class but no lifecycle callbacks will be called and no view created.

You need to use the TestComponentBuilder instead:

describe('ComponentToTest', () => {
  let component: ComponentToTest;
  beforeEach(async(inject([TestComponentBuilder], (tcb: TestComponentBuilder) => {
    tcb.createAsync(ComponentToTest).then((fixture: ComponentFixture<ComponentToTest>) => {
      fixture.detectChanges();
      component = fixture.componentInstance;
      // component.ngOnInit(); // called by `fixture.detectChanges()`
    });
  })));

  it('should initialise children', () => {
    expect(component.children).toBeDefined();
  });
});

To ensure that the test doesn't end before all async execution is done also use async() like already mentioned by Thierry.

Upvotes: 2

Thierry Templier
Thierry Templier

Reputation: 202286

I would leverage the async function:

it('should initialise children', async(inject([ComponentToTest], (component: ComponentToTest, service: Service) => {
  return service.getData().toPromise().then(() => {
    expect(component.children).toBeDefined();
  });
})));

From the documentation:

Wraps a test function in an asynchronous test zone. The test will automatically complete when all asynchronous calls within this zone are done. Can be used to wrap an inject call

Upvotes: 0

Related Questions