Comments (32)
One approach is to use Container.set and Container.get in your tests.
@Service()
class MyGlobalClass() {
foo(){}
}
@Service()
class MyOtherClass( public global: MyGlobalClass) {}
Then in your test, you do
describe('MyOtherClass' , () => {
it('tests the class', () =>{
Container.set(MyGlobalClass, { foo: jest.fn().mockReturnValue(true)})
const otherClass = Container.get(MyOtherClass)
expect(otherClass.global.foo()).toBe(true)
})
})
from typedi.
Keep in mind that with @Inject
you won't be able to access your injected dependency in the constructor body, the injection is done lazily
from typedi.
Do you clear your container after each test? It could be that the container instanciated your service already, so you setting the dependency of your service does nothing since Container.get
will just get back the already created instance anyway.
from typedi.
Container.reset()
should do it for you
from typedi.
Yeah, I've tried that with the same effect. No worries mate, I'm done for today anyways, tomorrow I will try some other ideas
Thank you so much
from typedi.
pinging @NoNameProvided for additional ideas
from typedi.
I did exactly that and suddenly it stopped working:
Container.set('example-api-client', new TestApiClient())
const service = Container.get(ServiceUsingExampleApiClient)
console.log(service.exampleApiClient) // original service, not TestApiClient one set using container
I cannot get it to work, and it doesn't have any sense whatsoever to stop working on me like that
from typedi.
how are you injecting ExampleApiClient into ServiceUsingExampleApiClient ?
from typedi.
Could you create a repo with this? Its hard to see whats wrong without context.
from typedi.
I cannot publish code from my work, and this is not reproducible since it doesnt work in that one only case, for now I can say that I've tried using Service() via type, and named Service() syntax (Service('name-of-service')) along with @Inject('name-of-service') and it doesnt work in either way.
from typedi.
import 'reflect-metadata'
import { Service } from 'typedi'
@Service('pocket-api-client')
export class PocketApiClient {
call = async (...args: any[]): Promise<any> => {
console.log('CALL ORIGINAL')
return { json: async () => Promise.resolve({ data: 'example', id: '123' }), ok: true }
}
}
import 'reflect-metadata'
import { Inject, Service } from 'typedi'
import { PocketApiClient } from '../../../../lib/api-client/pocket-api-client'
...
@Service()
export class AddPocketSectionItem {
@Inject('pocket-api-client')
public pocketApiClient: PocketApiClient
constructor() {}
...
import 'reflect-metadata'
import { Container } from 'typedi'
import { AddPocketSectionItem } from '../services/request'
...
class TestApiClient {
public call = async (...args) => {
console.log('CALL TEST')
return { json: async () => Promise.resolve({ data: 'example', id: '123' }), ok: true }
}
}
...
describe('POCKET SERVICE add-pocket-section-item', () => {
it('responds with data fetched from api service', async () => {
Container.set('pocket-api-client', new TestApiClient())
const service = Container.get(AddPocketSectionItem)
console.log(service.pocketApiClient) // original pocketApiClient, not TestApiClient
What is funny, is that when I remove type from Inject() statement, it says:
ServiceNotFoundError: Service with "pocket-api-client" identifier was not found in the container. Register it before usage via explicitly calling the "Container.set" function or using the "@Service()" decorator.
Needless to say, I've tried using only Service() and type syntax, without names, and it doesn't work - injected value in AddPocketSectionItem is just undefined
from typedi.
It's weird because it works for me locally. Is there a chance your lib is in a monorepo?
from typedi.
Yes, but other apps are not using any DI containers. It stopped working when I added some new code (unrelated to this), using httpContext for acquiring some headers from each request, and setting getConfig() method in a container via Container.set('get-config', getConfig). It's tested and mockable
import { Config } from 'config'
import { Container } from 'typedi'
import { getConfig } from '../get-config'
import { mockedConfig } from './config-mock'
describe('getConfig method', () => {
it('responds with generated config', async () => {
const config = getConfig()
expect(config).toEqual(mockedConfig)
})
it('responds with generated config while using default app name', async () => {
const config = getConfig('moda')
expect(config).toEqual(mockedConfig)
})
it('is injectable', async () => {
Container.set('MOCK_GET_CONFIG', getConfig)
const injectedConfig = Container.get('MOCK_GET_CONFIG') as () => Config
const config = injectedConfig()
expect(config).toEqual(mockedConfig)
})
})
those tests are passing
from typedi.
The problem is the metadata storage of the DI, technically it's tied to your node_modules folder. If your app happens to be in a monorepo that has separate node_module instances for each part, you are kinda out of luck. The Container instance will be different for each. Could you verify if that's the case?
from typedi.
First of all, thanks for your help, it's really kind of you. I verified, my API only uses one exclusive node_modules.
from typedi.
What is funny, is that when I remove type from Inject() statement, it says:
That is expected, if you never reference the class that registers the token it won't be available in the container.
Needless to say, I've tried using only Service() and type syntax, without names, and it doesn't work - injected value in AddPocketSectionItem is just undefined
This is the more interesting part, because if the container is not able to find the token it should throw an error.
The troubling part is that if you mess up the mock registration in the test, it should return the original not undefined.
from typedi.
In test it returns mocked one, in service's body there is undefined, without error
from typedi.
Do you use @Inject
or constructor injection?
from typedi.
describe('POCKET SERVICE add-pocket-section-item', () => {
it('responds with data fetched from api service', async () => {
Container.set(PocketApiClient, new TestApiClient())
const service = Container.get(AddPocketSectionItem)
const mockedApiClient = Container.get(PocketApiClient)
console.log(service.pocketApiClient)
console.log(mockedApiClient)
console.log
undefined
at console.log (../../node_modules/@sentry/src/integrations/console.ts:51:1)
console.log
TestApiClient { call: [Function (anonymous)] }
at console.log (../../node_modules/@sentry/src/integrations/console.ts:51:1)
from typedi.
Tried both, it still doesn't work (sorry, I got excited because test passed)
from typedi.
After using constructor injection (without @Inject() decorator) it just injects original dependency instead of mock:
Container.set(PocketApiClient, new TestApiClient())
const service = Container.get(AddPocketSectionItem)
const mockedApiClient = Container.get(PocketApiClient)
console.log(service.pocketApiClient) // original pocketApiClient, not TestApiClient
console.log(mockedApiClient)
console.log
PocketApiClient { call: [Function (anonymous)] }
at console.log (../../node_modules/@sentry/src/integrations/console.ts:51:1)
console.log
TestApiClient { call: [Function (anonymous)] }
at console.log (../../node_modules/@sentry/src/integrations/console.ts:51:1)
from typedi.
@Service()
export class AddPocketSectionItem {
constructor(public pocketApiClient: PocketApiClient) {}
@Service()
export class PocketApiClient {
call = async (...args: any[]): Promise<any> => {
console.log('CALL ORIGINAL')
return { json: async () => Promise.resolve({ data: 'example', id: '123' }), ok: true }
}
}
from typedi.
That ought to be the case here! How do I clear container? I searched in documentation, and I didn't see anything like "reset"
from typedi.
It doesn't work unfortunately, still getting original service instead of mocked one
from typedi.
Just for the sake of the test, could you remove the @Service
decorator from PocketApiClient and see what happens?
from typedi.
ServiceNotFoundError: Service with "MaybeConstructable<PocketApiClient>" identifier was not found in the container. Register it before usage via explicitly calling the "Container.set" function or using the "@Service()" decorator.
from typedi.
Ok at this point all I can think of is that something else creates that service for you beforehand somewhere. Could you try to reset the container in the test itself before setting the mock?
from typedi.
You could add a log before the Container.get
to make sure it's actually throwing there not beforehand
from typedi.
describe('POCKET SERVICE add-pocket-section-item', () => {
it('responds with data fetched from api service', async () => {
Container.reset()
Container.set(PocketApiClient, new TestApiClient())
const service = Container.get(AddPocketSectionItem)
console.log(service.pocketApiClient)
const mockedApiClient = Container.get(PocketApiClient)
console.log(mockedApiClient)
its respectively undefined
and TestApiClient { call: [Function (anonymous)] }
from typedi.
maybe as an experiment try changing the implementation of AddPocketSectionItem to
export class AddPocketSectionItem {
public pocketApiClient = Container.get(PocketApiClient)
constructor() {}
from typedi.
See this
and also, remove all manual mocks of injected dependencies in the test code.
When asserting these injected class's methods in Jest tests, always use jest.spyOn
instead of mocking them.
from typedi.
One approach is to use Container.set and Container.get in your tests.
@Service() class MyGlobalClass() { foo(){} } @Service() class MyOtherClass( public global: MyGlobalClass) {}
Then in your test, you do
describe('MyOtherClass' , () => { it('tests the class', () =>{ Container.set(MyGlobalClass, { foo: jest.fn().mockReturnValue(true)}) const otherClass = Container.get(MyOtherClass) expect(otherClass.global.foo()).toBe(true) }) })
It works for me!
I was also missing the import 'reflect-metadata' in my test file.
from typedi.
Related Issues (20)
- fix: Constructor Injection fail in vitest/jest HOT 3
- question: adding custom proxy traps in class constructors and instances HOT 2
- fix: non-reconstructable services are erased when imported HOT 1
- question: Stage 3 decorator support (available since TS 5.0)
- question: How to debug circular dependency injections ?
- Question: Service Injection working incorrectly HOT 1
- question: is this project/repo dead? HOT 5
- question: <Implementing repository injection using typedi-typeorm.> HOT 3
- question: <Is it possible to adapt your DI to work with modules (not classes)?> HOT 1
- question: HOT 3
- fix: Error when running in bunjs runtime HOT 2
- fix: not working function dependency injection examples from docs HOT 3
- question: Inject Private Property HOT 2
- Using ContainerInstance in the service constructor causes TypeScript build breaks HOT 1
- question: identifier was not found in the container HOT 2
- question: Using typedi with monorepo HOT 3
- question: Using Typedi with Vite - Returning ContainerInstance2 HOT 3
- question: How to define a third-party dependency HOT 5
- question: Using Token with Generics failed in stongly typying
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from typedi.