Typeorm 装饰器不是函数

Posted

技术标签:

【中文标题】Typeorm 装饰器不是函数【英文标题】:Typeorm decorator is not a function 【发布时间】:2020-07-29 20:55:46 【问题描述】:

我有以下要测试的控制器:

class Album 
  public static getAlbums(): Promise<AlbumModel[]> 
    return getRepository(AlbumModel).find( relations: ['pictures'] );
  

它与模型相关联,我将 typeorm 与装饰器一起使用。这就是我使用 Jest 时的问题所在

import  Entity, PrimaryGeneratedColumn, Column, JoinTable, ManyToMany  from 'typeorm';
import  PicturesModel  from '../pictures/pictures.model';

@Entity(
  name: 'T_ALBUM_AL',
  synchronize: true,
)
export class AlbumModel 
  @PrimaryGeneratedColumn(
    name: 'AL_id',
  )
  id: number;

  @Column(
    name: 'AL_name',
  )
  name: string;

  @ManyToMany(() => PicturesModel, (picture: PicturesModel) => picture.id)
  @JoinTable(
    name: 'TJ_PICTURE_ALBUM_PA',
    joinColumn: 
      name: 'AL_id',
      referencedColumnName: 'id',
    ,
    inverseJoinColumn: 
      name: 'PI_id',
      referencedColumnName: 'id',
    ,
  )
  pictures: PicturesModel[];

为了测试这个控制器,我正在使用 ts-jest。这是我的单元测试:

import Album from './album.controller';

const getRepMock = jest.fn(() => (
  find: jest.fn().mockImplementation(),
));

jest.mock('typeorm', () => (
  getRepository: getRepMock,
));

describe('Album', () => 
  it('Should work', () => 
    Album.getAlbums();
    expect(getRepMock).toBeCalled();
  );
);

当我运行测试时,我收到以下错误:

 FAIL  src/api/v1/album/album.test.ts
  ● Test suite failed to run

    TypeError: typeorm_1.PrimaryGeneratedColumn is not a function

       6 | )
       7 | export class PicturesModel 
    >  8 |   @PrimaryGeneratedColumn(
         |    ^
       9 |     name: 'PI_id',
      10 |   )
      11 |   id: number;

它有什么问题?

这是我的 package.json 的一部分

  "jest": 
    "preset": "ts-jest",
    "testEnvironment": "node",
    "coveragePathIgnorePatterns": [
      "/node_modules/"
    ]
  ,
  "dependencies": 
    "@types/dotenv": "^8.2.0",
    "body-parser": "^1.19.0",
    "dotenv": "^8.2.0",
    "express": "^4.17.1",
    "express-boom": "^3.0.0",
    "glob": "^7.1.6",
    "morgan": "^1.10.0",
    "mysql": "^2.14.1",
    "pg": "^7.18.2",
    "reflect-metadata": "^0.1.10",
    "ts-jest": "^25.3.1",
    "typeorm": "0.2.24"
  ,
  "devDependencies": 
    "@types/express": "^4.17.3",
    "@types/express-boom": "^3.0.0",
    "@types/glob": "^7.1.1",
    "@types/jest": "^25.2.1",
    "@types/morgan": "^1.9.0",
    "@types/node": "^8.0.29",
    "@types/supertest": "^2.0.8",
    "@typescript-eslint/eslint-plugin": "^2.24.0",
    "@typescript-eslint/parser": "^2.24.0",
    "eslint": "^6.8.0",
    "eslint-config-airbnb-base": "^14.1.0",
    "eslint-import-resolver-alias": "^1.1.2",
    "eslint-plugin-import": "^2.20.1",
    "eslint-plugin-module-resolver": "^0.16.0",
    "jest": "^25.3.0",
    "nodemon": "^2.0.2",
    "supertest": "^4.0.2",
    "ts-node": "3.3.0",
    "typescript": "3.3.3333",
    "typescript-eslint": "^0.0.1-alpha.0"
  

【问题讨论】:

【参考方案1】:

你需要模拟出typeorm,像这样:

import  Repository, SelectQueryBuilder  from 'typeorm';
import  mock  from 'jest-mock-extended';

const repositoryMock = mock<Repository<any>>();
const qbuilderMock = mock<SelectQueryBuilder<any>>();

jest.mock('typeorm', () => 
    qbuilderMock.where.mockReturnThis();
    qbuilderMock.select.mockReturnThis();
    repositoryMock.createQueryBuilder.mockReturnValue(qbuilderMock);

    return 
        getRepository: () => repositoryMock,

        BaseEntity: class Mock ,
        ObjectType: () => ,
        Entity: () => ,
        InputType: () => ,
        Index: () => ,
        PrimaryGeneratedColumn: () => ,
        Column: () => ,
        CreateDateColumn: () => ,
        UpdateDateColumn: () => ,
        OneToMany: () => ,
        ManyToOne: () => ,
    
)

【讨论】:

如何将其添加到现有项目中?它自己的文件和导入的 .spec.ts 文件,还是完全其他的文件? 你只需要将它添加到测试文件中【参考方案2】:

我喜欢尽可能多地留下 typeorm 自动模拟,所以我选择了不同的方法。我模拟所有typeorm,然后只覆盖我正在使用的特定装饰器:

jest.mock('typeorm')
mocked(EntityRepository).mockImplementation(() => jest.fn())
mocked(Entity).mockImplementation(() => jest.fn())
mocked(Column).mockImplementation(() => jest.fn())
mocked(PrimaryGeneratedColumn).mockImplementation(() => jest.fn())
mocked(CreateDateColumn).mockImplementation(() => jest.fn())
mocked(UpdateDateColumn).mockImplementation(() => jest.fn())
mocked(DeleteDateColumn).mockImplementation(() => jest.fn())
mocked(OneToMany).mockImplementation(() => jest.fn())
mocked(ManyToOne).mockImplementation(() => jest.fn())
mocked(TableInheritance).mockImplementation(() => jest.fn())
mocked(ChildEntity).mockImplementation(() => jest.fn())

然后,当我想模拟 .find() 之类的某些方法时,我模拟 EntityManager 的特定方法:

mocked(EntityManager.prototype.getCustomRepository).mockReturnValue(repository);
mocked(EntityManager.prototype.transaction as transactionOp).mockImplementation(async (runInTransaction) => runInTransaction(entityManager));
mocked(EntityManager.prototype.findOne).mockResolvedValue(
  id: 1,
  name: 'Cool Album',
);

【讨论】:

以上是关于Typeorm 装饰器不是函数的主要内容,如果未能解决你的问题,请参考以下文章

检查函数是不是有装饰器

理解Python装饰器

Python学习笔记012——装饰器

python--装饰器

NestJS 测试:装饰器不是函数

检查函数是不是被称为装饰器