Skip to content

Better way of mocking the injected repository #49

Open
@jasonlimantoro

Description

@jasonlimantoro

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    type: featureIssues related to new features.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions