typeorm-typedi-extensions
typeorm-typedi-extensions copied to clipboard
Better way of mocking the injected repository
Function getRepository
from src/decorators/InjectRepository.ts seems to be problematic.
I dealt with it like this:
// UserRepository.ts
import { EntityRepository, Repository } from 'typeorm';
@Service()
@EntityRepository(User)
export class UserRepository extends Repository<User> { }
// UserResolver.ts
import { Service } from 'typedi';
import { InjectRepository } from 'typeorm-typedi-extensions';
@Service()
export class UserResolver {
@InjectRepository()
private readonly userRepository!: UserRepository;
}
// UserResolver.test.ts
import { ConnectionManager } from 'typeorm';
// mocked repository
class MockUserRepository {
static findOne: jest.MockedFunction<typeof UserRepository.prototype.findOne>;
static setupMocks() {
this.findOne = jest.fn().mockResolvedValue(undefined);
}
}
describe('UserResolver class', () => {
beforeAll(() => {
// This, as we know, is not enough for mocking @InjectRepository()
// Container.set(UserRepository, MockUserRepository);
// this will be used only once during the initial import so there is no need to put this in beforeEach
Container.set(ConnectionManager, {
has: (connectionName: string) => true,
get: (connectionName: string) => ({
getRepository: (entityType: any) => {
console.warn(`No mock repository found for ${entityType}`);
},
getMongoRepository: (entityType: any) => {
console.warn(`No mock repository found for ${entityType}`);
},
getTreeRepository: (entityType: any) => {
console.warn(`No mock repository found for ${entityType}`);
},
getCustomRepository: (repositoryType: any) => {
switch (repositoryType) {
case UserRepository:
return MockUserRepository; // here we mock our repository
default:
console.warn(`No mock repository found for ${repositoryType}`);
}
},
}),
});
});
beforeEach(() => {
MockUserRepository.setupMocks();
});
it('should pass', async () => {
MockUserRepository.findOne.mockResolvedValue({});
await <test>
expect(MockUserRepository.findOne).toHaveBeenCalledWith({ email: '...' });
});
});
if you inject repository as:
@InjectRepository(User)
private readonly userRepository!: Repository<User>;
then this should work:
Container.set(ConnectionManager, {
has: (connectionName: string) => true,
get: (connectionName: string) => ({
getRepository: (entityType: any) => {
switch (entityType) {
case User:
return mockUserRepository;
default:
console.warn(`No mock repository found for ${entityType}`);
}
},
}),
});
Originally posted by @kajkal in https://github.com/typeorm/typeorm-typedi-extensions/issues/33#issuecomment-623199794
A better way of mocking is clearly needed. It seems that the selected solution above does not fully solve the issue since you need to also mock the entire ConnectionManager
. In other words, with the above solution only, any getConnection()
calls would fail as it is not mocked (not to mention any other methods).
Better way means, you only mock what you need to mock. Pretty much like
Container.set('UserRepository', mockUserRepository)
would be ideal.
I am not actively maintaining this lib so won't implement this, but if someone comes up with a solution we can give it a go.
For other readers: if you don't need a custom repository then you can just use getRepository()
from TypeORM, eg:
import { getRepository } from 'typeorm';
import { Service } from 'typedi';
import { User } from 'your-code';
@Service()
export class UserResolver {
private readonly userRepository = getRepository(User);
}
can someone tell me is this looks good or not?. it works.
// base.repository.ts
import { EntityTarget, Repository as Repo } from 'typeorm';
import { dataSource } from '..';
export const Repository = <Entity>(target: EntityTarget<Entity>) => {
return class extends Repo<Entity> {
constructor() {
super(target, dataSource.createEntityManager());
}
};
};
// user.repository.ts
import { Service } from 'typedi';
import { User } from '../entities';
import { Repository } from './base.repository';
@Service()
export class UserRepository extends Repository(User) {
findByEmailOrFail(email: string) {
return this.findOneByOrFail({ email });
}
}
Apart from this i did't have to do anything. and i am not using this package.