Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to replace a component used in @viewChildren for a test double?

Suppose I have a component I want to test that uses a very complex component. Furthermore it calls some of its methods using references obtained by @viewChildren. For example

    @Component({
        moduleId: module.id,
        selector: 'test',
        template: '<complex *ngFor='let v of vals'></complex>' ,
    })
    export class TestComponent{
    vals = [1,2,3,4]
    @ViewChildren(ComplexComponent) cpxs : QueryList<ComplexComponent>
    // ....
    }

How can I replace the complex-component for a test double in `TestBed'?

Something like

@Component({
  moduleId : module.id,
  selector: 'complex', template: ''
})
class ComplexComponentStub {
}

describe('TestComponent', () => {
  beforeEach( async(() => {
    TestBed.configureTestingModule({
      declarations : [ComplexComponentStub, TestComponent],
    });
it('should have four sons',()=>{
   let fixture = TestBed.createComponent(TestComponent);
   let comp    = fixture.componentInstance as TestComponent;
   fixture.detectChanges();
   expect(comp.cpxs.length).toBe(4);
});

    //....
}));

For a full example see the plnkr http://plnkr.co/edit/ybdrN8VimzktiDCTvhwe?p=preview

like image 595
user2832323 Avatar asked Nov 29 '16 00:11

user2832323


People also ask

How do you mock a component in Angular unit testing?

A mock component in Angular tests can be created by MockComponent function. The mock component respects the interface of its original component, but all its methods are dummies. To create a mock component, simply pass its class into MockComponent function.

How can we get an instance of the component to be tested?

The TestBed. createComponent() method is used to create an instance of the AppComponent. The spec then uses expect and matcher functions to see if the component produces the expected behavior. As a result, the spec will either pass or fail.


2 Answers

You can use reflect-metadata features to do it working:

it('should have four sons', () => {
   const propMetadata = Reflect['getMetadata']('propMetadata', FatherComponent);
   var originType = propMetadata.cpxs[0].selector;
   propMetadata.cpxs[0].selector = ComplexComponentStub; // Replace ViewChild Type

   let fixture = TestBed.createComponent(FatherComponent);

   let comp = fixture.componentInstance as FatherComponent;
   fixture.detectChanges();
   expect(comp.cpxs.length).toBe(4);

   propMetadata.cpxs[0].selector = originType; // reset ViewChild
});

Test in Plunker

You can read more about decorators and about reflect-metadata here:

  • Angular 2, decorators and class inheritance
like image 178
yurzui Avatar answered Oct 10 '22 20:10

yurzui


if u just want to test the function in child component are called or not u can try this

component.ts

@ViewChildren('childComponent') childComponents: QueryList<YourComponent>;

component.spec.ts

  it('Your test name', () => {
    component.dashboard = dashboardMock; // ur using ngFor so u need to populate it first. u can mock it with ur own data
    fixture.detectChanges(); // component will render the ngfor
    const spies = [];
    component.childComponents.toArray().forEach((comp) => {
      comp.childFunction = () => { // Mock the function.
         return 'Child function called!';
      };
      const tempSpy = {
        spyKey: spyOn(comp, 'functionToBeMocked') // spy the mocked function
      };
      spies.push(tempSpy); // add to array
    });
    component.functionToTest(); // call the function u wish to test
    spies.forEach((spy) => { 
      expect(spy.spyKey).toHaveBeenCalled(); // check if child function are called
    });
  });
like image 31
Mirza Setiyono Avatar answered Oct 10 '22 20:10

Mirza Setiyono