【问题标题】:How to use jest.spyOn with NestJS Transaction code on Unit Test如何在单元测试中将 jest.spyOn 与 NestJS 事务代码一起使用
【发布时间】:2020-12-19 04:34:10
【问题描述】:

NestJS 在 (https://docs.nestjs.com/techniques/database#transactions) 上提供了一个示例事务代码,我现在想针对该代码创建单元测试脚本。以下是一些依赖文件:

@Entity()
export class User {
  @PrimaryGeneratedColumn()
  id: number;

  @Column({type: 'text', name: 'first_name'})
  firstName: string;

  @Column({type: 'text', name: 'last_name'})
  lastName: string;

  @Column({name: 'is_active', default: true})
  isActive: boolean;
}
@Injectable()
export class UsersService {
  constructor(
    private connection: Connection
  ) {}

  async createMany(users: User[]): User[] {
    const queryRunner = this.connection.createQueryRunner();

    await queryRunner.connect();
    await queryRunner.startTransaction();
    try {
      const user1 = await queryRunner.manager.save(users[0]);

      await queryRunner.commitTransaction();

      return [user1];
    } catch (err) {
      // since we have errors lets rollback the changes we made
      await queryRunner.rollbackTransaction();
    } finally {
      // you need to release a queryRunner which was manually instantiated
      await queryRunner.release();
    }
  }
}

这是单元测试脚本。我即将完成,但我仍然从await queryRunner.manager.save(users[0]) 获得undefinedjest.spyOn 设置如下:

describe('UsersService', () => {
  let usersService: UsersService;
  let connection: Connection;

  class ConnectionMock {
    createQueryRunner(mode?: "master" | "slave"): QueryRunner {
      const qr = {
        manager: {},
      } as QueryRunner;
      qr.manager;
      Object.assign(qr.manager, {
        save: jest.fn()
      });
      qr.connect = jest.fn();
      qr.release = jest.fn();
      qr.startTransaction = jest.fn();
      qr.commitTransaction = jest.fn();
      qr.rollbackTransaction = jest.fn();
      qr.release = jest.fn();
      return qr;
    }
  }

  beforeEach(async () => {
    const module: TestingModule = await Test.createTestingModule({
      providers: [UsersService, {
        provide: Connection,
        useClass: ConnectionMock,
      }],
    }).compile();

    usersService = module.get<UsersService>(UsersService);
    connection = module.get<Connection>(Connection);
  });

  it('should be defined', () => {
    expect(usersService).toBeDefined();
  });

  it('should return expected results after user create', async () => {
    const user1: User = { id: 1, firstName: 'First', lastName: 'User', isActive: true };
    const queryRunner = connection.createQueryRunner();
    // I would like to make the following spyOn work
    jest.spyOn(queryRunner.manager, 'save').mockResolvedValueOnce(user1);
    expect(await appService.createMany([user1])).toEqual([user1]);
  });
});

【问题讨论】:

    标签: node.js jestjs nestjs


    【解决方案1】:

    connection.createQueryRunner(); 将返回 QueryRunner 的差异实例。

    这意味着const queryRunner = connection.createQueryRunner();queryRunner 不会在const queryRunner = this.connection.createQueryRunner();createMany 函数中引用queryRunner

    那么你的模拟 jest.spyOn(queryRunner.manager, 'save').mockResolvedValueOnce(user1); 没有意义。

    保持简单,将QueryRunner 设为“全局”变量,然后将其绑定到ConnectionMock,这意味着当我们调用createQueryRunner 时将拥有相同的“变量”。

    更新了beforeEach 内容,ConnectionMock 类。

    describe('UsersService', () => {
      let usersService: UsersService;
      let connection: Connection;
    
      const qr = {
        manager: {},
      } as QueryRunner;
    
      class ConnectionMock {
        createQueryRunner(mode?: "master" | "slave"): QueryRunner {
          return qr;
        }
      }
    
      beforeEach(async () => {
        // reset qr mocked function
        Object.assign(qr.manager, {
          save: jest.fn()
        });
        qr.connect = jest.fn();
        qr.release = jest.fn();
        qr.startTransaction = jest.fn();
        qr.commitTransaction = jest.fn();
        qr.rollbackTransaction = jest.fn();
        qr.release = jest.fn();
    
        const module: TestingModule = await Test.createTestingModule({
          providers: [UsersService, {
            provide: Connection,
            useClass: ConnectionMock,
          }],
        }).compile();
    
        usersService = module.get<UsersService>(UsersService);
        connection = module.get<Connection>(Connection);
      });
    
      it('should be defined', () => {
        expect(usersService).toBeDefined();
      });
    
      it('should return expected results after user create', async () => {
        const user1: User = { id: 1, firstName: 'First', lastName: 'User', isActive: true };
        const queryRunner = connection.createQueryRunner();
        // I would like to make the following spyOn work
        jest.spyOn(queryRunner.manager, 'save').mockResolvedValueOnce(user1);
        expect(await appService. createMany([user1])).toEqual([user1]);
      });
    });
    
    

    【讨论】:

    • 如果我不想使用 jest.spyOn 可以像 queryRunner.manager.save.mockResolvedValueOnce(...) 一样直接使用吗
    • 你能不能也展示一下如何测试异常
    猜你喜欢
    • 2021-03-13
    • 2014-04-09
    • 2011-01-14
    • 2022-06-17
    • 2016-02-22
    • 1970-01-01
    • 2014-02-24
    • 2019-11-22
    • 1970-01-01
    相关资源
    最近更新 更多