Nestjs mongoose unit test: TypeError: <functionName> is not a function

Nestjs mongoose unit test: TypeError: <functionName> is not a function

根据这个github repo我们要测试一个用户样本
考虑这个测试文件:

const mockUser = (
  phone = '9189993388',
  password = 'jack1234',
  id = '3458',
): User => ({
  phone,
  password,
  _id: new Schema.Types.ObjectId(id),
});

const mockUserDoc = (mock?: Partial<User>): Partial<IUserDocument> => ({
  phone: mock?.phone || '9189993388',
  password: mock?.password || 'jack1234',
  _id: mock?._id || new Schema.Types.ObjectId('3458'),
});

const userArray = [
  mockUser(),
  mockUser('Jack', '9364445566', 'jack@gmail.com'),
];

const userDocArray = [
  mockUserDoc(),
  mockUserDoc({
    phone: '9364445566',
    password: 'jack1234',
    _id: new Schema.Types.ObjectId('342'),
  }),
  mockUserDoc({
    phone: '9364445567',
    password: 'mac$',
    _id: new Schema.Types.ObjectId('425'),
  }),
];

describe('UserRepository', () => {
  let repo: UserRepository;
  let model: Model<IUserDocument>;

  beforeEach(async () => {
    const module: TestingModule = await Test.createTestingModule({
      providers: [
        UserRepository,
        {
          provide: getModelToken('User'),
          // notice that only the functions we call from the model are mocked
          useValue: {
            new: jest.fn().mockResolvedValue(mockUser()),
            constructor: jest.fn().mockResolvedValue(mockUser()),
            find: jest.fn(),
            findOne: jest.fn(),
            update: jest.fn(),
            create: jest.fn(),
            remove: jest.fn(),
            exec: jest.fn(),
          },
        },
      ],
    }).compile();

    repo = module.get<UserRepository>(UserRepository);
    model = module.get<Model<IUserDocument>>(getModelToken('User'));
  });

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

  afterEach(() => {
    jest.clearAllMocks();
  });

  it('should return all users', async () => {
    jest.spyOn(model, 'find').mockReturnValue({
      exec: jest.fn().mockResolvedValueOnce(userDocArray),
    } as any);
    const users = await repo.findAll({});
    expect(users).toEqual(userArray);
  });

  it('should getOne by id', async () => {
    const userId = '324';
    jest.spyOn(model, 'findOne').mockReturnValueOnce(
      createMock<Query<IUserDocument, IUserDocument>>({
        exec: jest.fn().mockResolvedValueOnce(
          mockUserDoc({
            _id: new Schema.Types.ObjectId(userId),
          }),
        ),
      }) as any,
    );
    const findMockUser = mockUser('Tom', userId);
    const foundUser = await repo.findById(new Schema.Types.ObjectId(userId));
    expect(foundUser).toEqual(findMockUser);
  });

这是用户文档文件:

export interface IUserDocument extends Document {
  _id: Schema.Types.ObjectId;
  email?: string;
  password: string;
  firstName?: string;
  lastName?: string;
  nationalCode?: string;
  phone: string;
  address?: string;
  avatar?: string;
}

第 1 和第 2 个测试通过,但第 3 个测试抛出:

TypeError: this.userModel.findById is not a function 同样接口是从mongoose Document扩展的,findById函数在测试中无法识别。

这是 the github repo 可用的。 因此,我们将不胜感激。

请注意,在您的 UserModel 模拟中,您没有为 findById

提供模拟函数
{
  provide: getModelToken('User'),
  // notice that only the functions we call from the model are mocked
  useValue: {
    new: jest.fn().mockResolvedValue(mockUser()),
    constructor: jest.fn().mockResolvedValue(mockUser()),
    find: jest.fn(),
    findOne: jest.fn(),
    update: jest.fn(),
    create: jest.fn(),
    remove: jest.fn(),
    exec: jest.fn(),
    findById: jest.fn(), // <-------------- Add this
  },
},

您模拟的那组方法中需要有一个 findById 方法。

顺便说一句,我需要通过 new 调用构造函数,所以我更喜欢像这样定义一个模拟 class:

class UserModelMock {
  constructor(private data) {}
  new = jest.fn().mockResolvedValue(this.data);
  save = jest.fn().mockResolvedValue(this.data);
  static find = jest.fn().mockResolvedValue(mockUser());
  static create = jest.fn().mockResolvedValue(mockUser());
  static remove = jest.fn().mockResolvedValueOnce(true);
  static exists = jest.fn().mockResolvedValue(false);
  static findOne = jest.fn().mockResolvedValue(mockUser());
  static findByIdAndUpdate = jest.fn().mockResolvedValue(mockUser());
  static findByIdAndDelete = jest.fn().mockReturnThis();
  static exec = jest.fn();
  static deleteOne = jest.fn().mockResolvedValue(true);
  static findById = jest.fn().mockReturnThis();
}