首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >NestJS TypeORM单元测试模拟getConnection.transaction

NestJS TypeORM单元测试模拟getConnection.transaction
EN

Stack Overflow用户
提问于 2022-01-23 13:01:50
回答 1查看 2.3K关注 0票数 1

我正在为NestJS应用程序编写UT。我在模拟甚至测试事务内部函数的逻辑方面有问题。

这是来自服务的一段代码,它获取一个连接并开始使用transactionEntityManager与DB交互。

我希望测试像本例中那样的函数的内部逻辑(我有几个类似的例子)。

代码语言:javascript
运行
复制
import { getConnection } from 'typeorm';
@Injectable()
export class UsersService {

    async getUsers(user: User): Promise<{ users: User[], organizations: MinimalOrg[] }> {
        const result: { users: User[], organizations: MinimalOrg[] } = { users: [], organizations: [] };
        // getConnection() is imported from 'typeorm'
        await getConnection().transaction(async (transactionEntityManager) => {
            if (!user || !Utils.isAdmin(user)) {
                throw new UnauthorizedException('You have no permission');
            }

            result.users = await transactionEntityManager
                .createQueryBuilder(User, 'user')
                .leftJoin('user.image', 'image')
                .leftJoin('user.organization', 'organization')
                .select([
                    'user.id',
                    'user.email',
                ])
                .addSelect(['organization.id', 'organization.name'])
                .addSelect(['image.id', 'image.fileName'])
                .getMany();

            result.organizations = (await transactionEntityManager
                .createQueryBuilder(Organization, 'org')
                .select(['org.id', 'org.name'])
                .getMany()) as MinimalOrg[];
        });
        return result;
    }
}

我已经尝试过在Unit测试中使用这种方法,但是我无法使它工作。

代码语言:javascript
运行
复制
jest.mock('typeorm',()=>({
    transaction:jest.fn()
}));

任何帮助都将非常感谢。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2022-03-13 11:37:26

经过几天的调查和你的反馈,我得出这个解决方案的步骤如下:

将connection).

  • Injected用于服务@InjectConnection() private readonly connection: Connection.

  • decorate的构造器中,方法具有@Transaction(),并添加服务方法@TransactionManager() _manager?.

的一个新参数

服务方法现在看起来如下所示:

代码语言:javascript
运行
复制
    @Transaction()
    async getUsers(
        user: User,
        @TransactionManager() _manager?
    ): Promise<{ users: User[], organizations: MinimalOrg[] }> {
        const result: { users: User[], organizations: MinimalOrg[] } = { users: [], organizations: [] };
        const transactionEntityManager = this.connection.manager;
        if (!user || !Utils.isAdmin(user)) {
            throw new UnauthorizedException('You have no permission');
        }
        result.users = await transactionEntityManager
            .createQueryBuilder(User, 'user')
            .leftJoin('user.image', 'image')
            .leftJoin('user.organization', 'organization')
            .select([
                'user.id',
                'user.email',
            ])
            .addSelect(['organization.id', 'organization.name'])
            .addSelect(['image.id', 'image.fileName'])
            .getMany();
        result.organizations = (await transactionEntityManager
            .createQueryBuilder(Organization, 'org')
            .select(['org.id', 'org.name'])
            .getMany()) as MinimalOrg[];
        return result;
    }

在spec文件中,重要的是使用数据库配置设置导入,如下所示:

代码语言:javascript
运行
复制
/* eslint-disable import/no-extraneous-dependencies */
import { Logger } from '@nestjs/common';
import { Test } from '@nestjs/testing';
import { getConnectionToken, TypeOrmModule, } from '@nestjs/typeorm';
import { UserRole } from 'gn-models';
import { ConnectionOptions, getConnection } from 'typeorm';

import { User } from '../models/user.entity';
import { UsersService } from './users.service';

// #region Mock
const createQueryBuilder: any = {
    createQueryBuilder: jest.fn().mockImplementation(() => createQueryBuilder),
    select: jest.fn().mockImplementation(() => createQueryBuilder),
    andWhere: jest.fn().mockImplementation(() => createQueryBuilder),
    addSelect: jest.fn().mockImplementation(() => createQueryBuilder),
    leftJoinAndSelect: jest.fn().mockImplementation(() => createQueryBuilder),
    innerJoin: jest.fn().mockImplementation(() => createQueryBuilder),
    leftJoin: jest.fn().mockImplementation(() => createQueryBuilder),
    groupBy: jest.fn().mockImplementation(() => createQueryBuilder),
    where: jest.fn().mockImplementation(() => createQueryBuilder),
    findOne: jest.fn().mockImplementation(() => null),
    save: jest.fn().mockImplementation(() => null),
    getOne: jest.fn().mockImplementation(() => null),
    getMany: jest.fn().mockImplementation(() => null),
    getRawMany: jest.fn().mockImplementation(() => null)
}
// #endregion

describe('UsersService', () => {
    let srv: UsersService;
    beforeEach(async () => {
        const moduleRef = await Test.createTestingModule({
            imports: [
                TypeOrmModule.forRoot({
                    type: "sqlite",
                    database: ":memory:",
                    dropSchema: true,
                    synchronize: true,
                    logging: false,
                    name: 'default'
                } as ConnectionOptions)
            ],
            providers: [
                UsersService,
                {
                    provide: getConnectionToken(),
                    useValue: {
                        manager: createQueryBuilder,
                        ...createQueryBuilder
                    }
                },
            ],
        }).compile();

        // Disable log error prints 
        jest.spyOn(Logger, 'error').mockReturnValue();

        srv = moduleRef.get<UsersService>(UsersService);
    });

    afterEach(async () => {
        jest.clearAllMocks();
        await getConnection().close();
    });


    it('should test getUsers', async () => {
        const userRecord = { rdp: '1', userName: 'aaa', roles: [UserRole.SuperAdmin] } as User;

        const res = await srv.getUsers(userRecord);
        expect(res).toBeTruthy();
        expect(res).toEqual({ users: null, organizations: null })
    });

    it('should test getUsers if the user is not admin', async () => {
        const userRecord = { rdp: '1', userName: 'aaa', roles: [] } as User;
        expect.assertions(1);
        const res = srv.getUsers(userRecord);
        await expect(res).rejects
            .toThrowError('You have no permission');
    });
});

注意,管理器设置在beforeEach上,因为连接允许在测试中模拟实体管理器,并且仍然在服务方法const transactionEntityManager = this.connection.manager;中使用。

我已经设置了await getConnection().close(); at afterEach,因此每个测试都将独立运行。如果连接已经打开,jest将无法运行具有相同连接名(默认)的其他测试。

我并不是说这是最好的解决方案,但在投入大量时间和精力来完成这项工作后,它对我有效。

我希望它能帮助你为你的项目写UT!

票数 -1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/70822310

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档