I've encountered a challenge while attempting to write a Jasmine/Karma test for an Angular 6 app. The test is for a service in my application that relies on another service with private properties and methods, causing my tests to consistently fail.
While the code functions correctly during runtime, I'm struggling to find the right approach for testing it. I've experimented with spies for the private methods and properties, as well as changing them to public, but so far, nothing has worked. It's evident that there's something crucial that I'm missing.
At the moment, my primary focus is not on testing DependencyService
. Instead, I aim to successfully test the function doSomething
within MyService
. Currently, this function always returns null
, leading to a test output of
Failed: Cannot read property 'subscribe' of null
. Interestingly, removing the if
statement in postData()
resolves the issue, and the test passes as expected.
It seems like I might be overlooking something significant in terms of spying, as my test appears to be overly dependent on the dependency service or values from localStorage
.
I need guidance on how to effectively mock/spy on the checkAuth
and isAuth
methods within my service's dependency. More specifically, how do I properly test doSomething()
to ensure that the test remains isolated to the MyService
service?
export class MyService {
constructor(private depService: DependencyService) { }
public doSomething(additionalPayload: Object) {
const payload = { ...additionalPayload, modified: true };
return this.depService.postData('/api/endpoint', payload);
}
}
export class DependencyService {
constructor(private httpClient: HttpClient) { }
private isAuth: boolean = false;
private checkAuth() {
const token = localStorage.get('token');
if (token !== null) {
this.isAuth = true;
} else {
this.isAuth = false;
}
}
postData(url, body): Observable<any> {
this.checkAuth();
if (!this.isAuth) {
return null;
}
return this.httpClient.post(url, body);
}
}
The myservice.spec.ts
currently failing:
describe('MyService', () => {
let httpTestingController: HttpTestingController;
let myService: MyService;
let dependencyServiceSpy: jasmine.SpyObj<DependencyService>;
beforeEach(() => {
const dependencyServiceSpyObj = jasmine.createSpyObj('DependencyService', ['postData']);
TestBed.configureTestingModule({
imports: [ HttpClientTestingModule ],
providers: [
MyService,
{ provide: DependencyService, useValue: dependencyServiceSpyObj },
]
});
httpTestingController = TestBed.get(HttpTestingController);
myService = TestBed.get(MyService);
dependencyServiceSpy = TestBed.get(DependencyService);
});
afterEach(() => {
httpTestingController.verify();
});
it('#doSomething should post some data', async(() => {
const payloadData: Object = {
name: 'Ash',
food: 'donut'
};
const responseData: Object = {
success: true,
msg: 'Payload received'
};
// HELP HERE ↓
// need to spy/mock dependencyService.isAuth so that it is `true`
// otherwise inside `postData` the if statement will always return a `null` value
// ...spy/mock `localStorage`?
dependencyServiceSpy.postData.and.returnValue(/* return http observable so that .subscribe can be called */);
myService.doSomething(payloadData).subscribe(data => {
expect(data).toEqual(responseData);
}, fail);
const req = httpTestingController.expectOne('/api/endpoint');
expect(req.request.method).toEqual('POST');
expect(req.request.body).toEqual({ ...payloadData, modified: true });
expect(dependencyServiceSpy.postData.calls.count()).toBe(1);
expect(dependencyServiceSpy.postData.calls.mostRecent().returnValue).toBe(responseData);
req.flush(responseData);
}));
});