Search code examples
javascripttypescriptunit-testingjasminespyon

How to spy on a function called inside class object


I want to test if this.service.someMethod is called using jasmine spy.

Source file:

// src.ts
import { Service } from 'some-package';

export class Component {
   service = new Service();

   callMethod() {
      this.service.thatMethod();
   }
}

Spec file:

// src.spec.ts
import { Component } from './src';

describe('test', () => {
   it('calls thatMethod of service', () => {
      let comp = new Component();

      spyOn(comp.service, 'thatMethod').and.callThrough();

      comp.callMethod();

      expect(comp.service.thatMethod).toHaveBeenCalled();
   });
});

Output:

Failed test: Expected comp.service.thatMethod to have been called.


Solution

  • I would suggest you to refactor your code and take advantage of IoC (inversion of control) pattern. That means that you have to get rid of Service dependency in your Component class and inject it manually, like this:

    export class Component {
       constructor(service) {
           this.service = service;
       }
    
       callMethod() {
         this.service.thatMethod();
       }
    }
    
    // Elsewhere in your code
    import { Service } from 'some-package';
    const component = new Component(new Service());
    

    This approach will allow you to test your components effectively with Service mock:

    import { Component } from './src';
    
    describe('test', () => {
        it('calls thatMethod of service', () => {
            const service = jasmine.createSpyObj('service', ['thatMethod']);
            let comp = new Component(service);
    
            comp.callMethod();
            expect(service.thatMethod).toHaveBeenCalled();
       });
    });