Angular2 - test that a injected private service method has been called
Asked Answered
V

1

5

First, I understand that I should not be testing a private method, however I feel there are times that I should test if a private method was called based on some logic.

I'm building a wrapper service around the angular 2 http service so that I can do global error handling without having to write the code into every http request.

A small sample of the class is like so:

export class ApiService {
  constructor(private http: Http, private error: Error, private apiConfig: ApiConfig) {}

  public get(url: string, options: RequestOptionsArgs={}): Observable<any> {
   return this.http.get(url, options).catch((error: any) => {
     this.error.dispatch(error);
     return error;
   });
  }
}

If I wanted to test the get method and mock respond with an Error response so that the catch fires I CAN'T test if the error.dispatch method was called because this is private. I can't spyOn a private method.

You could argue that I'm really just testing that 'catch' is being fired and if it does obviously the method inside it would fire but that would just be testing the http service itself. I want to be sure that error.dispatch has been added as a call if 'catch' is fired and fail if for some reason it's removed by another developer. Maybe this is a bad way of testing it? I open to other suggestions.

I can Mock the private class like so:

{ provide: Error, useClass: FakeError }

but the blueprint of the ApiService class stays the same and the now FakeError service is still private. In Java you can create the instance yourself and inject that, because you own it you can access it. I guess I'm trying to find a way to do that in jasmine/typescript/angular2 ??

Villein answered 21/9, 2016 at 18:0 Comment(2)
You can spy on a private method.Consecrate
@estus that may be true but I can't test it out because I'm using typescript and if I try to assert like so: expect(service.error.dispatch).to... then the typescript linter blows up because I don't have access to service.error because it's privateVillein
A
7

This is where spies can be helpful. If the real Error can be instantiated without DI, you could do

let error;
beforeEach(()=>{
   error = new Error();
   spyOn(error, 'dispatch');

  TestBed.configureTestingModule({
    providers: [
      { provide: Error, useValue: error }
    ]
  });
});

it('', ()=> {
  expect(error.dispatch).toHaveBeenCalled();
});

Or you can forget the Error class and provide a mock.

class FakeError {
  dispatch = jasmine.createSpy('dispatch');
}

Just use it like above. You don't need to call spyOn like above though. Here, dispatch is already a spy.

Ai answered 21/9, 2016 at 18:18 Comment(1)
Awesome, that worked great thanks. One question, why do I have to put the spy up in the beforeEach and not in the it block. I tried that and it didn't work, I was surprised. Any idea?Villein

© 2022 - 2024 — McMap. All rights reserved.