Unit testing with private service injected using jasmine angular2

20,818

Solution 1

You don't spy on the service tied to your TestBed. Get the service from your Testbed

beforeEach(() => {
  TestBed.configureTestingModule({
    providers: [ServiceToTest ,
      { provide: ServiceInjected, useValue: serviceInjectedStub }]
  });
  injectedService = TestBed.get(ServiceInjected);
});

And test on it

spyOn(injectedService, 'configure').and.returnValue(/* return same data type here */);
// ...
expect(injectedService.configure).toHaveBeenCalled();

Solution 2

Or you can use jasmine.createSpyObj and provide it with useValue like bellow:

describe('YourComponent', () => {

  let serviceInjectedSpy: jasmine.SpyObj<ServiceInjected>;

  beforeEach(async(() => {

     // notice here
     serviceInjectedSpy = jasmine.createSpyObj('ServiceInjected', ['configure']);

     TestBed.configureTestingModule({
        declarations: [YourComponent],
        providers: [
           {provide: ServiceInjected, useValue: serviceInjectedSpy}
        ],
        imports: [
         ...
        ]
     }).compileComponents().then(() => {
        fixture = TestBed.createComponent(YourComponent);
        component = fixture.componentInstance;
     });
  });

  it('should assert my test', () => {
       serviceInjectedSpy.configure.and.returnValue(/* what you want */);
       component.init();
       expect(serviceInjectedSpy.configure).toHaveBeenCalled();
  });

});

Solution 3

Use this:

spyOn(serviceInjectedStub, 'configure').and.returnValue(config); // config is a mock

Share:
20,818

Related videos on Youtube

RjHiruma
Author by

RjHiruma

Updated on July 09, 2022

Comments

  • RjHiruma
    RjHiruma over 1 year

    I have a problem trying to unit test an angular service. I want to verify that this service is properly calling another service that is injected into it.

    Lets say I have this ServiceToTest that injects ServiceInjected:

    ServiceToTest .service.ts

    @Injectable()
    export class ServiceToTest  {
        constructor(private _si: ServiceInjected) {}
        public init() {
          this._si.configure();
        }
    
    }

    ServiceInjected.service.ts

    @Injectable()
    export class ServiceInjected {
        constructor() {}
        public configure() {
        /*Some actions*/
        }
    
    }

    With these services, now I write my unit test:

    const serviceInjectedStub = {
      configure(): void {}
    }
    
    
    describe('ServiceToTest service Test', () => {
      beforeEach(() => {
        TestBed.configureTestingModule({
          providers: [ServiceToTest ,
            { provide: ServiceInjected, useValue: serviceInjectedStub }]
        });
      });
      
      it('should be initialize the service injected', inject([ServiceToTest],
        (tService: ServiceToTest) => {
          spyOn(serviceInjectedStub, 'configure');
          tService.init();
          expect(serviceInjectedStub.configure).toHaveBeenCalled();
        }));

    I expected my test to be positive, however I receive the following error:

    Expected spy configure to have been called.

    On the other hand, it works OK if I set the injected service public in this way:

    private _si: ServiceInjected by public si: ServiceInjected
    
    • JB Nizet
      JB Nizet almost 6 years
      See github.com/angular/angular/issues/10788. You could simply inject the real service and spy on it. Or not use DI at all and use new ServiceToTest(stub) to test the service. Or use useFactory as explained in the issue.
  • RjHiruma
    RjHiruma almost 6 years
    Thx dude !! Holiday's at 17h for me today ! :p
  • Admin
    Admin almost 6 years
    No problem, enjoy them :D
  • amansinghgusain
    amansinghgusain about 5 years
    this is an important piece of information I was missing, thanks mate
  • Admin
    Admin about 5 years
    @amansinghgusain as a side note, you're not forced to use the Test bed to get it, you can simply use the array notation in your component : injectedService = component['myService']. Works even with private attributes
  • HisDivineShadow
    HisDivineShadow almost 5 years
    For some reason this didn't work for me. I still get that the service was not called and it certainly gets called in ngOnInit.
  • Admin
    Admin almost 5 years
    Then please provide a minimal reproducible example of your issue and create a new question, as this answer has been verified and works.
  • xinthose
    xinthose over 2 years
    get is deprecated, we now use inject