Reputation: 74
I'm using Node with TypeScript, TypeDI and Jest. I'm creating services that depend on each other, let's say:
@Service()
export class MainService{
constructor(private secondService: SecondService){}
public someMethod(someString: string) // implementation depends on secondService
}
@Service()
export class SecondService{
constructor(private thirdService: ThirdService){}
}
@Service()
export class ThirdService{
constructor(){}
}
I want to test MainService, but to instantiate it I need to pass dependency and that dependency needs another dependecy. I tried to do this like, it works, but is ugly:
const secondService = new SecondService(new ThirdService());
jest
.spyOn(secondService, "someMethod")
.mockImplementation((someString: string) => {
// do something
return something;
});
const mainService = new MainService(secondService);
// use mainService in tests
Of course creating new instance of dependency is not always an option, and defienetly not an option when it has many dependencies.
I think it should look more like:
const secondService = SomeMockFactory.create(SecondService);
but i can't find any way to create mock while cutting off dependencies. I tried using
const secondService = jest.genMockFromModule("path/to/second/service");
but after trying to spyOn secondService methods TS is throwing error that "someMethod" is not a function. What am I missing / doing wrong? Do I need some other library than Jest?
Upvotes: 2
Views: 5713
Reputation: 74
After a while I found out how to do this using default jest behaviour.
First, you need to create mock of SecondService in path/to/second/service/__mocks__
, like:
// path/to/second/service/__mocks__/SecondService.ts
const mock = jest.fn().mockImplementation(() => ({
async thingSecondServiceDoInFirstService(
param: number
): number {
return 1;
}));
export default mock;
SecondService has to be default export, like:
// path/to/second/service/SecondService.ts
@Service()
export default class SecondService {
constructor(private thirdService: ThirdService) {}
async thingSecondServiceDoInFirstService(
param: number
): number {
return this.thirdService.thingThirdServiceDoInSecond(param);
}
}
In test file you have to use jest.mock before importing SecondService, and then create SecondService instance from mock:
jest.mock("path/to/second/service/SecondService");
import SecondService from "path/to/second/service/SecondService";
import MainService from "path/to/main/service/MainService";
describe("Test main service", () => {
const SecondServiceMock = <jest.Mock<SecondService>>SecondService;
let secondService = new SecondServiceMock();
beforeEach(() => {
mainService = new MainService(secondService);
});
// test logic...
}
As requested, ThirdService is not needed anymore.
Upvotes: 2