1

I have this simple method which needs to be tested:

  public onLayerContainerClick(event: Event): void {
    event.stopPropagation();
    event.preventDefault();
    if (event.srcElement.classList.contains('dpm-info__layerContainer')) {
     this.closeInfoLayer(event);
    }
  }

My objective is actually to increase code coverage for the component. If I test is like this:

  it( 'should close on info container click', () => {
    spyOn( component, 'onLayerContainerClick' );
    const el: DebugElement = fixture.debugElement.query( By.css( '.dpm-info__layerContainer' ) );
    el.triggerEventHandler( 'click', null );
    expect( component.onLayerContainerClick ).toHaveBeenCalled();
  } );

the test is OK, but instanbul says "function not covered". So I guess, I need to call the function explicitly? To do that, I need to have a full event object, including (at least) srcTarget property. How do I define such an event within the unit test?

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
pop
  • 3,464
  • 3
  • 26
  • 43

2 Answers2

3

You have to call the function with a mocked event in the test. component.onLayerContainerClick({ srcElement: { value: 'mock_value' } });

And then write what you expect

expect( component.closeInfoLayer ).toHaveBeenCalled();

Maybe this https://angular.io/guide/testing#triggereventhandler might help as well

DrNio
  • 1,936
  • 1
  • 19
  • 25
  • The challenge is the Typescript, which expects the argument passed in to `onLayerContainerClick` to be of type `Event`. Just passing an object with `srcTarget` property doesn't cut it - I get a type error. – pop Dec 08 '17 at 10:29
  • ok, can you post also the `beforeEach ` at the beginning of each test ? btw, if you try component.onLayerContainerClick({ srcElement: { value: 'mock_value' } }); or do u get errors ? – DrNio Dec 08 '17 at 10:35
  • 1
    @pop Not a challenge. The types are there to help you, not to create obstacles. You can pass any argument you need with `arg` or `arg`. – Estus Flask Dec 08 '17 at 10:40
  • 1
    yes, for mocking input data it shouldn't be a problem – DrNio Dec 08 '17 at 10:42
  • Well, I am over the first hurdle, and am able to construct an object of type Event, just as you guys say. However (here we go), this line in the component `event.srcElement.classList.contains` expects `classList` to be of type `DOMTokenList` which has its own methods and properties (see here https://stackoverflow.com/questions/29172515/constructing-a-domtokenlist-domsettabletokenlist-instance). – pop Dec 08 '17 at 11:02
1

Thanks to estus and DrNio I've come up with this test, which makes istanbul code coverage happy and doesn't have any type problems:

  it( 'should close on info container click', () => {
    spyOn( component, 'closeInfoLayer' );
    const el: HTMLElement = fixture.debugElement.query(By.css('.dpm-info__layerContainer')).nativeElement;
    const mockEvent: Event = <Event><any>{
      srcElement: {
        classList: el.classList
      },
      stopPropagation: <any>( ( e: any ) => { /**/ }),
      preventDefault: <any>( ( e: any ) => { /**/ }),
    };

    component.onLayerContainerClick( mockEvent );
    expect( component.closeInfoLayer ).toHaveBeenCalled();
  } );
pop
  • 3,464
  • 3
  • 26
  • 43
  • i think you don't need the `spyOn( component, 'closeInfoLayer' );`. I think this is used mostly when you want to check function calls of external services, ex: `spyOn(myService, 'aMethodThere');` – DrNio Dec 08 '17 at 15:25
  • well, I need *something* to test again. no variable is set in that method and the only outcome is the function call. so this is what I test - it being called. how else? – pop Dec 08 '17 at 15:27
  • if u delete `spyOn( component, 'closeInfoLayer' );` it should work, cause it will pass the if statement since u mock the event. give it a try and let us know – DrNio Dec 08 '17 at 15:31
  • what should I `expect` then? just deleting `spyOn` and leaving `expect` will throw error. – pop Dec 08 '17 at 15:33
  • can you post the error here ? i think if it throws error it means that it didn't pass the if statement and the `closeInfoLayer ` was not called – DrNio Dec 08 '17 at 15:38