Skip to content

Instantly share code, notes, and snippets.

@krystalmonolith
Last active April 17, 2019 17:29
Show Gist options
  • Save krystalmonolith/5fc717157df4b192e03565f09af3b019 to your computer and use it in GitHub Desktop.
Save krystalmonolith/5fc717157df4b192e03565f09af3b019 to your computer and use it in GitHub Desktop.
Refactored Angular Service Testing
// THE REFACTORED SERVICE TEST
import {ActivityTypeService} from './activity-type.service';
import {UrlService} from '..';
import {httpGetSpecDefinitions} from '../base/service-test-base';
const TEST_VALUE: Array<object> = [{x: 'obj1'}, {x: 'obj2'}];
describe('ActivityTypeService',
httpGetSpecDefinitions((spy) => new ActivityTypeService(spy, new UrlService()), TEST_VALUE));
// THE SERVICE BEING TESTED
import {Injectable} from '@angular/core';
import {HttpClient} from '@angular/common/http';
import {Observable} from 'rxjs';
import {UrlService} from '../url/url.service';
@Injectable({
providedIn: 'root'
})
export class ActivityTypeService {
private activityURL: string;
constructor(private http: HttpClient, private urlService: UrlService) {
this.urlService.endpoint('MYSERVER', 'activitytype').subscribe(url => this.activityURL = url);
}
get(): Observable<any> {
return this.http.get(this.activityURL);
}
}
// THE OUTPUT WHEN THE TEST IS RUN
[11:16:58] I/launcher - Running 1 instances of WebDriver
[11:16:58] I/direct - Using ChromeDriver directly...
Jasmine started
ActivityTypeService
✓ should be created
✓ get() mocked with [{"x":"obj1"},{"x":"obj2"}]
Executed 2 of 2 specs SUCCESS in 0.017 sec.
[11:17:00] I/launcher - 0 instance(s) of WebDriver still running
[11:17:00] I/launcher - chrome #01 passed
Process finished with exit code 0
// THE 'GUTS' OF THE TEST SETUP THAT WAS REFACTORED OUT OF THE TEST
import "jasmine";
import {Observable, of} from 'rxjs';
import {HttpClient} from '@angular/common/http';
import createSpyObj = jasmine.createSpyObj;
/**
* The IHttpTest interface is required to satisfy TypeScript type checking on the
* 'get' function of the service being tested.
* @type R The type being observed by the mocked HttpClient.get() observable.
*/
interface IHttpTest<R> {
get(): Observable<R>;
}
/**
* @type R The type being observed (returned) by the mocked HttpClient.get() observable.
* @type S The service class being tested, which is instantiated by the getService callback.
* @param getService A callback that takes a mocked HTTPClient spy and
* returns an instance of the service whose get() functionality is being tested.
* @param tv The test data value to be passed *and* validated as the value of type R.
*/
export function httpGetSpecDefinitions<R, S extends IHttpTest<R>>(getService: (Spy) => S, tv: any) {
return () => {
let httpClientSpy: { get: jasmine.Spy }
let service: S;
// IMPORTANT: DO NOT import beforeEach, causes error "this.runnable is not a function"
beforeEach(() => {
httpClientSpy = createSpyObj('HttpClient', ['get']);
service = getService(httpClientSpy);
});
it('should be created', () => {
expect(service).toBeTruthy();
});
it(`get() mocked with ${JSON.stringify(tv)}`, () => {
httpClientSpy.get.and.returnValue(of(tv));
service.get().subscribe(v => {
expect(v).toBe(tv);
});
});
};
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment