2

I have this code in a component template, which opens a ngx-bootstrap modal:

<button type="button"
        class="btn btn-primary"
        (click)="onOpenSharesModal(modal)">Click me!</button>
<ng-template #modal>
    <app-modal (closed)="onCloseSharesModal()"></app-modal>
</ng-template>

Component:

onOpenSharesModal(template: TemplateRef<any>) {
    this.modalRef = this.modalService.show(template, { class: 'modal-lg' });
}

Test:

it('should call onOpenSharesModal() when button clicked', () => {
  const button = fixture.debugElement.query(By.css('button'));
  const spy = spyOn(component, 'onOpenSharesModal');

  button.triggerEventHandler('click', null);
  fixture.detectChanges();

  expect(spy).toHaveBeenCalled();
});

I'm trying to test the component: I've been able to test that onOpenSharesModal() is being called, but I'd like to test if it was called with the modal template variable as argument. How can I do that?

Pizzicato
  • 1,473
  • 1
  • 16
  • 32

2 Answers2

2

You can use a spy to spy on the function and check what was passed as an argument. Let's assume your component is called MyComponent. In your unit test file you have (a bit shortened, but you should get the picture):

let myComponent: MyComponent = fixture.componentInstance;

// Set up a spy on your function
let spy = spyOn(myComponent, 'onOpenSharesModal').and.callThrough();

// After the function has been called somewhere in your component
expect(spy).toHaveBeenCalled();

// Check the arguments that were passed
expect(spy.calls.mostRecent().args[0]).toEqual(myComponent.modal);

This is assuming the modal template variable is accessible from your component.

Fabian Küng
  • 5,925
  • 28
  • 40
  • Thanks for the answer! The thing is that the question is precisely for the case in which the modal template variable is not accessible from the component – Pizzicato Jan 08 '19 at 13:45
  • 1
    Is there a reason why it should not be accessible? You could just access it via `@ViewChild('modal') modal`. If it is no accessible, then there is no way to really test whether exactly that template variable has been passed as an argument, as you cannot compare the passed argument to anything. – Fabian Küng Jan 09 '19 at 07:48
  • I just didn't want to add ViewChild to the component just to be able to test it, but I guess is the way to go. Thanks – Pizzicato Jan 09 '19 at 10:43
  • 1
    Nothing wrong with adding variables to your component to facilitate unit testing, just add a comment for other developers and you're all set. – Fabian Küng Jan 09 '19 at 10:48
1

First you will want to add the following line to your component so that you can reference the modal template variable:

@ViewChild('modal') myModal: TemplateRef<any>; // for testing

Now you can reference the component variable 'myModal' in your test:

it('should call onOpenSharesModal() when button clicked', () => {
  const button = fixture.debugElement.query(By.css('button'));
  const spy = spyOn(component, 'onOpenSharesModal');

  button.triggerEventHandler('click', null);
  fixture.detectChanges();

  expect(spy).toHaveBeenCalled();
  expect(spy).toHaveBeenCalledWith(component.myModal);
});

Here is a working StackBlitz to demonstrate.

dmcgrandle
  • 5,934
  • 1
  • 19
  • 38
  • 1
    While I agree this would work, I don't think adding code to your to be able to test is valid. Testing and code have their own concerns and if we start writing code to satisfy tests then what are we really accomplishing with our real code. You have the ability to get the viewChild from the fixture however so this would allow you to remove the ViewChild reference in your component and still complete your tests! :) – Jessy Feb 04 '19 at 16:09