【问题标题】:SpyOn TypeORM repository to change the return value for unit testing NestJSSpyOn TypeORM 存储库更改单元测试 NestJS 的返回值
【发布时间】:2019-11-27 16:55:09
【问题描述】:

我想对我的 TypeORM 数据库调用进行单元测试。我已经用有效数据模拟了我所有的 TypeORM 存储库。但我想 SpyOn 存储库并更改 TypeORM 的返回值。我该怎么做?

import {INestApplication} from '@nestjs/common';
import {Test} from '@nestjs/testing';
import {CommonModule} from '@src/common/common.module';
import {AuthService} from './auth.service';
import {Repository} from 'typeorm';
import {V3User} from '@src/database/entity/user.v3entity';
    
describe('AuthService', () => {
    let service: AuthService;
    let app: INestApplication;
        
    beforeEach(async () => {
        const module = await Test.createTestingModule({
            imports: [CommonModule.forRoot(`${process.env.DEV_ENV}`)],
            providers: [
                AuthService,     
                {provide: 'V3USER_REPOSITORY', useValue: mockRepositoryV3User()},
            ],
        }).compile();
    
        app = module.createNestApplication();
        await app.init();
    
        service = module.get<AuthService>(AuthService);
    });
    

    
    it('test auth service - with non existing user in v3 db', async () => {
                
        jest.spyOn(?????? , 'findOne').mockImplementation(() => undefined);
    
        const res = await service.loginUser("bad token");
    
        await expect(service.tokenBasedAuth('example bad token'))
            .rejects.toThrow('bad token exception');
    });
});

对于正常的测试用例,我像这样模拟数据库:

export const mockRepositoryV3User = () => ({
    metadata: {
        columns: [],
        relations: [],
    },
    findOne: async () =>
        Promise.resolve({
            id: 3,
            email: 'email@example.com',
            first_name: 'david',
            last_name: 'david',
            last_login: '2019-07-15',
            date_joined: '2019-07-15',
        }),
});

【问题讨论】:

    标签: nestjs typeorm


    【解决方案1】:

    好的,在终于开始测试和尝试想法之后,我发现这是一个有效的策略

    1. 假设我们已经设置了一个PhotoEntity 具有基本属性,没有什么特别的(id、名称、描述等)
    import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm';
    
    @Entity()
    export class Photo {
      @PrimaryGeneratedColumn()
      id: number;
    
      @Column({ length: 500 })
      name: string;
    
      @Column('text')
      description: string;
    
      @Column()
      filename: string;
    
      @Column('int')
      views: number;
    
      @Column()
      isPublished: boolean;
    }
    
    1. 设置PhotoService,如下所示(超级基本,但它会说明这一点):
    import { Injectable } from '@nestjs/common';
    import { InjectRepository } from '@nestjs/typeorm';
    import { Repository } from 'typeorm';
    import { Photo } from './photo.entity';
    
    @Injectable()
    export class PhotoService {
      constructor(
        @InjectRepository(Photo)
        private readonly photoRepository: Repository<Photo>,
      ) {}
    
      async findAll(): Promise<Photo[]> {
        return await this.photoRepository.find();
      }
    }
    
    1. 我们可以useClass: Repository 这样我们就不必做任何繁重的设置存储库类以用于测试(存储库是从 TypeORM 包中导入的。然后我们可以从模块中获取存储库并将其保存为便于模拟的值,并像这样设置我们的测试:
    import { Test, TestingModule } from '@nestjs/testing';
    import { PhotoService } from './photo.service';
    import { getRepositoryToken } from '@nestjs/typeorm';
    import { Photo } from './photo.entity';
    import { Repository } from 'typeorm';
    
    describe('PhotoService', () => {
      let service: PhotoService;
      // declaring the repo variable for easy access later
      let repo: Repository<Photo>;
    
      beforeEach(async () => {
        const module: TestingModule = await Test.createTestingModule({
          providers: [
            PhotoService,
            {
              // how you provide the injection token in a test instance
              provide: getRepositoryToken(Photo),
              // as a class value, Repository needs no generics
              useClass: Repository,
            },
          ],
        }).compile();
    
        service = module.get<PhotoService>(PhotoService);
        // Save the instance of the repository and set the correct generics
        repo = module.get<Repository<Photo>>(getRepositoryToken(Photo));
      });
    
      it('should be defined', () => {
        expect(service).toBeDefined();
      });
    
      it('should return for findAll', async () => {
        // mock file for reuse
        const testPhoto: Photo =  {
          id: 'a47ecdc2-77d6-462f-9045-c440c5e4616f',
          name: 'hello',
          description: 'the description',
          isPublished: true,
          filename: 'testFile.png',
          views: 5,
        };
        // notice we are pulling the repo variable and using jest.spyOn with no issues
        jest.spyOn(repo, 'find').mockResolvedValueOnce([testPhoto]);
        expect(await service.findAll()).toEqual([testPhoto]);
      });
    });
    
    1. 针对指定文件或针对所有测试运行测试
    ▶ npm run test -- photo.service
    
    > nestjs-playground@0.0.1 test ~/Documents/code/nestjs-playground
    > jest "photo.service"
    
     PASS  src/photo/photo.service.spec.ts
      PhotoService
        ✓ should be defined (17ms)
        ✓ should return for findAll (4ms)  < -- test passes with no problem
    
    Test Suites: 1 passed, 1 total
    Tests:       2 passed, 2 total
    Snapshots:   0 total
    Time:        3.372s, estimated 4s
    Ran all test suites matching /photo.service/i.
    

    【讨论】:

    • 优秀。完美运行。
    • 如何测试查询是否像 .createQueryBuilder('image').offset(pagination.offset).limit(pagination.limit).getManyAndCount();
    • 对于类似的事情,你需要很多 mockReturnThis() 类型的笑话函数,最后一个是 mockresolvedValue()。你可能最终会得到几个模拟或间谍,每个都被链接减去最后一个,所以一个jest.spyOn(repo, 'createQueryBuilder'),一个jest.spyOn(repo, 'offset'), etc, and your repo should probably set up an initial createQueryBuilder` 方法。为了更深入,这个问题真的应该作为一个单独的问题提出
    • @asus 正如 Discord 上所讨论的,您试图在同步函数上使用 mockResolvedValueOnce。相反,您应该使用mockReturnValueOnce
    • @JayMcDoniel 出于某种原因我收到此错误Error: Cannot spy the find property because it is not a function; undefined given instead 我确定我按照您的步骤操作
    猜你喜欢
    • 2021-06-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-12-04
    • 2021-08-07
    • 1970-01-01
    • 2021-06-16
    相关资源
    最近更新 更多