Reputation: 1994
I'm building a browser library for consumption by our clients where they can include it in their site and utilize it to access our services, etc.
I am trying to set up unit testing for the library. After having some trouble getting Karma and Jasmine set up I did some reading and it appears that Karma isn't a good option. I settled attempting to use Vitest. It seems to be preferred for testing TypeScript modules with specs also written in TypeScript.
I'm having trouble figuring out the best way to do mocking of dependencies of a "service" that I've written.
I have a class that is a service (if you were looking at this from a DI perspective like Angular). Its constructor takes the dependencies it requires to perform its functionality. I'm now attempting to write the unit tests for this service. I want to mock the dependencies that the service will consume in a controlled manner without having to mock the whole dependency tree.
If I was writing this with Jasmine I would be writing something like:
// Leaving out the imports
describe('MyService fetch', () => {
let dependentServiceSpy: jasmine.SpyObj<DependentService>;
beforeEach(() => {
dependentServiceSpy = jasmine.createSpyObj<DependentService>('DependentService', ['methodToMock']);
});
it('should do this functionality', async () => {
const mockedResult = 'result';
dependentServiceSpy.methodToMock.and.callFake(async () => { return mockedResult; });
// Here I can create my service without having to mock the dependencies that
// may be required for DependentService.
const myService = new MyService(dependentServiceSpy);
const result = await myService.fetch();
expect(dependentServiceSpy.methodToMock).toHaveBeenCalled();
expect(result).toEqual(mockedResult);
});
});
I'm having trouble converting this to how Vitest would like the test written.
With Vitest it seems like I would need to do something like:
// leaving out the vitest imports
import { DependentService } from './dependent.service.ts';
describe('MyService fetch', () => {
beforeEach(() => {
vi.mock('./dependent.service.ts', () => {
return {
DependentService: vi.fn(() => ({
methodToMock: vi.fn(async () => { return 'result'; }),
})),
}
});
});
it('should do this functionality', async () => {
// I don't understand how to test this.
const myService = new MyService(new DependentService(/* any dependencies would need to be 'injected' here */));
const result = await myService.fetch();
expect(result).toEqual('result');
});
});
This seems off to me. At least, it seems overly complex and unneeded. It appears that if the DependentService
took a dependency then I would also have to import its dependencies and possibly do another import mock. This would need to happen down the whole chain if there were multiple classes each with their own dependencies, I'm assuming. This scheme wouldn't be executing the sub-dependent's code as the mock implementation of the necessary function would short circuit the call chain. However, I don't think that qualifies as closed box testing.
Instead of worrying about the dependencies of the MyService
class I would need to insure the entire tree was always modeled. Again, this seems like I'm viewing this wrong because who would want to do that?
I often want a suite of tests where I'm going to be using a dependency's method and want to modify that return value during each test case. I would like the beforeEach
set up to define that the method is mocked and then in the test case be able to set its return value. If the code I was testing used a method or property on the mocked dependency that wasn't defined I would want it to error. This would alert a dev that they're doing something that wasn't covered in the previously written unit test.
Is there a prescribed way of doing service mocking such that it isn't necessary to build the entire dependency tree? Are there better ways to model this so that needing to do this isn't necessary?
Upvotes: 0
Views: 44