Reputation: 1551
I have a problem and would appreciate a helping hand.
We have a service with a constructor using optional params like so
@Injectable()
export class OurService {
constructor(
private someService: SomeService,
@Optional() someOption: SomeOption,
) {
if(someOption) {
this.someService.doSomethingWith(someOption);
}
// ...do other things
}
// ...other methods
}
Now I want to test that and get good coverage which means there need to be tests where OurService gets instantiated one time with, the other time without someOption and also I want to expect that someService.doSoemthingWith has been called.
I usually setup my testing-module like so
let service: OurService;
let someService: SomeService;
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [],
declarations: [],
providers: [
{ provide: SomeService, useClass: MockSomeService },
],
}).compileComponents();
}));
beforeEach(() => {
service = TestBed.get(AuthService);
someService = TestBed.get(SomeService);
});
Now I have strong difficulty trying to get all my tests right. How do I test these 3 things?
Here my attempts (among heaps of others..):
install a spy and see if method has been called
it('blabla', () => {
spyOn(someService, 'doSomethingWith');
// I guess the spy is too late because TestBed get already called the constructor
expect(someService.doSomethingWith).toHaveBeenCalled();
});
attempt to add a provider for this one test
it('blabla', () => {
spyOn(someService, 'doSomethingWith');
TestBed.overrideProvider(SomeOption, { useValue: MockSomeOption }); // MockSomeOption is defined somewhere
TestBed.compileComponents();
service = TestBed.get(SomeService);
// does not work
expect(someService.doSomethingWith).toHaveBeenCalled();
});
inject smeOption (dont know how, hence this code is wic)
it('blabla', inject([SomeService], (someService: SomeService) => {
//const injector = Injector.create({ providers: [{ provide: SomeOption, useValue: MockSomeOption }] });
spyOn(someService, 'doSomethingWith');
// ?? what now ??
expect(someService.doSomethingWith).toHaveBeenCalled();
});
Add someOption to the TestBed setup like so
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [],
declarations: [],
providers: [
{ provide: SomeService, useClass: MockSomeService },
{ provide: SomeOption, useClass: MockSomeOption },
],
}).compileComponents();
}));
// problem now is that it is always provided. and I can not test the else-path
How can I set up my service once as required by most tests, and then add the someOption in one test and call the constructor again so that my spy can do its secret espionage job?
Upvotes: 1
Views: 3046
Reputation: 43817
I'm not sure if it is exactly what you want but you can nest describe
...
describe('Our Service Tests', () => {
describe('Given an optional parameter', () => {
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [],
declarations: [],
providers: [
{ provide: SomeService, useClass: MockSomeService },
{ provide: SomeOption, useClass: MockSomeOption },
],
}).compileComponents();
}));
// Tests with the optional parameters
});
describe('Given no optional parameter', () => {
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [],
declarations: [],
providers: [
{ provide: SomeService, useClass: MockSomeService },
],
}).compileComponents();
}));
// Tests not using the optional parameter
});
});
If I only have one or two tests that require a unique setup I'll usually skip creating a describe for those tests. Instead I'll create one nested describe with a default setup for the majority of tests and then just add the unique tests to the outer describe and have them each do their own setup.
Upvotes: 2