I have a Typescript class that uses InversifyJS and Inversify Inject Decorators to inject a service into a private property. Functionally this is fine but I'm having issues figuring out how to unit test it. I've created a simplified version of my problem below.
In the Jasmine unit test, how can I swap out the injected RealDataService
with a FakeDataService
? If the property wasn't private I could create the component and assign a fake service but I am wondering if this is possible by using the IOC Container.
I initially followed this this example in the InversifyJS recipes page but quickly realised that the container they created isn't used in any class under test. Also, most of the code examples that I can see in the InversifyJS docs don't cover how to unit test it.
Here is a simplified version of the problem:
myComponent.ts
import { lazyInject, Types } from "./ioc";
import { IDataService } from "./dataService";
export default class MyComponent {
@lazyInject(Types.IDataService)
private myDataService!: IDataService;
getSomething(): string {
return this.myDataService.get();
}
}
dataService.ts
import { injectable } from "inversify";
export interface IDataService {
get(): string;
}
@injectable()
export class RealDataService implements IDataService {
get(): string {
return "I am real!";
}
}
IOC Configuration
import "reflect-metadata";
import { Container, ContainerModule, interfaces, BindingScopeEnum } from "inversify";
import getDecorators from "inversify-inject-decorators";
import { IDataService, RealDataService } from "./dataService";
const Types = {
IDataService: Symbol.for("IDataService")
};
const iocContainerModule = new ContainerModule((bind: interfaces.Bind) => {
bind<IDataService>(Types.IDataService).to(RealDataService);
});
const iocContainer = new Container();
iocContainer.load(iocContainerModule);
const { lazyInject } = getDecorators(iocContainer);
export { lazyInject, Types };
Unit Tests
import { Container } from "inversify";
import { Types } from "./ioc";
import MyComponent from "./myComponent";
import { IDataService } from "./dataService";
class FakeDataService implements IDataService {
get(): string {
return "I am fake!";
}
}
describe("My Component", () => {
let iocContainer!: Container;
let myComponent!: MyComponent;
beforeEach(() => {
iocContainer = new Container();
iocContainer.bind(Types.IDataService).to(FakeDataService);
// How do I make myComponent use this iocContainer?
// Is it even possible?
myComponent = new MyComponent();
});
it("should use the mocked service", () => {
const val = myComponent.getSomething();
expect(val).toBe("I am fake!");
});
});