Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Better way of mocking the injected repository #49

Open
jasonlimantoro opened this issue Apr 14, 2021 · 2 comments
Open

Better way of mocking the injected repository #49

jasonlimantoro opened this issue Apr 14, 2021 · 2 comments
Labels
type: feature Issues related to new features.

Comments

@jasonlimantoro
Copy link

jasonlimantoro commented Apr 14, 2021

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 #33 (comment)

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.

@NoNameProvided NoNameProvided added the type: feature Issues related to new features. label Apr 20, 2021
@NoNameProvided
Copy link
Member

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);
}

@hardikkheni
Copy link

hardikkheni commented Jun 4, 2022

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: feature Issues related to new features.
Development

No branches or pull requests

3 participants