如何在Jest中模拟MongoDB查询的`.exec()`方法?

huangapple go评论73阅读模式
英文:

How to mock a MongoDB query .exec() in Jest?

问题

以下是您要翻译的部分:

// src/helpers/user.ts
import { UserModel } from "../models";

export const getUserByUsername = async (username: string) => {
  return UserModel.findOne({ username }).exec()
}
// src/models/index.ts
import { getModelForClass } from "@typegoose/typegoose";

export const UserModel = getModelForClass(User, { schemaOptions: { timestamps: true } });

以下是测试代码的部分:

// src/helpers/user.test.ts
describe("getUserByUsername():", () => {
  test('should return a user if username matches', async () => {
    const mockUser: User = await userFactory.build("user");

    const spy = jest.spyOn(UserModel, "findOne");
    spy.mockImplementationOnce(() => Promise.resolve(mockUser))

    const userByUsernameResult = usersHelper.getUserByUsername(mockUser.username);

    await expect(userByUsernameResult).resolves.toEqual(expect.objectContaining({ username: mockUser.username })); 
  });
});

在上面的代码中,我已经翻译了您提供的JavaScript代码。如果您需要进一步的帮助,请随时告诉我。

英文:

I would like to mock this async function with Jest in a Typescript Express env.

// src/helpers/user.ts
import { UserModel } from "../models";

export const getUserByUsername = async (username: string) => {
  return UserModel.findOne({ username }).exec()
}
// src/models/index.ts
import { getModelForClass } from "@typegoose/typegoose";

export const UserModel = getModelForClass(User, { schemaOptions: { timestamps: true } });

Here is my the test that fails:

// src/helpers/user.test.ts
describe("getUserByUsername():", () => {
  test('should return a user if username matches', async () => {
    const mockUser: User = await userFactory.build("user");

    const spy = jest.spyOn(UserModel, "findOne");
    spy.mockImplementationOnce(() => Promise.resolve(mockUser))

    const userByUsernameResult = usersHelper.getUserByUsername(mockUser.username);

    await expect(userByUsernameResult).resolves.toEqual(expect.objectContaining({ username: mockUser.username })); 
  });
});

The error message I get:

> expect(received).resolves.toEqual() Received promise rejected instead
> of resolved Rejected to value: [TypeError:
> models_1.UserModel.findOne(...).exec is not a function]

Could you please help me?

UPDATE: I use in some other tests the MongoDB Memory Server approach that works in this case too (see below). However, I would really like to know how to do it by mocking the .exec() (see above).

// src/helpers/user.test.ts
beforeAll(async () => {
  await mongoose.connect(process.env.MONGO_URL as string, { useNewUrlParser: true, useUnifiedTopology: true } as ConnectOptions, (err) => {
    if(err) {
      console.error(err);
      process.exit(1);
    }
  })
});

afterAll(async () => {
  await mongoose.connection.close();
});

describe("getUserByUsername():", () => {
  test('should return a user if username matches', async () => {
    const mockUser: User = await userFactory.build("user", {}, { setId: false });

    const validUser = new UserModel(mockUser);
    await validUser.save();

    const userByUsernameResult = usersHelper.getUserByUsername(mockUser.username);

    await expect(userByUsernameResult).resolves.toEqual(expect.objectContaining({ username: mockUser.username }));
  });

答案1

得分: 1

你的情况似乎与这个问题非常相似。

与那里给出的答案相似,你可以这样做:

UserModel.findOne = jest.fn().mockImplementationOnce(() => ({ exec: jest.fn().mockResolvedValueOnce(() => {/* 这里是你的值。*/})}));

为了详细说明并为答案添加一些价值:exec() 是在调用 UserModel.findOne() 上链接的方法。因此,你必须进行两个级别的模拟:一个是为 UserModel.findOne() 定义一个模拟实现,另一个是将 exec() 模拟为与 UserModel.findOne 链式调用,指定其返回的值。

如果你想在链式调用中继续模拟,你也必须为 exec() 定义一个模拟实现,使用 mockImplementationOnce 提供一个带有所需方法的对象等等。

英文:

Your case seems quite similar with this question.

In similarity to the answer given there, you can do:

UserModel.findOne = jest.fn().mockImplementationOnce(() => ({ exec: jest.fn().mockResolvedValueOnce(() => {/* Your value here.*/})}));

To elaborate and add some value to the answer: exec() is a method chained with the call on UserModel.findOne(). Therefore, you have to mock on two levels: one that defines a mock implementation for UserModel.findOne(), and one that mocks the exec() as a chain call with UserModel.findOne, specifying the value returned by it.

If you want continue mocking in the chain, you have to define a mock implementation for exec() as well using mockImplementationOnce, providing an object with the methods desired for the chain, etc.

答案2

得分: 0

我认为这个方法应该有效。

   const findOneMock = jest.spyOn(UserModel, 'findOne');
    findOneMock.mockResolvedValueOnce({ exec: jest.fn().mockResolvedValue(mockUser) } as any)

在类型错误的情况下,显式类型断言在模拟对象方法中的方法。

    findOneMock.mockResolvedValueOnce({
      exec: jest.fn().mockResolvedValue(mockUser) as unknown as () => Promise<User>,
    } as any);
英文:

I think this approach should work.

   const findOneMock = jest.spyOn(UserModel, &#39;findOne&#39;);
    findOneMock.mockResolvedValueOnce({ exec: jest.fn().mockResolvedValue(mockUser) } as any)

in case of type error explicit type assertions on mock object approach.

    findOneMock.mockResolvedValueOnce({
      exec: jest.fn().mockResolvedValue(mockUser) as unknown as () =&gt; Promise&lt;User&gt;,
    } as any);

huangapple
  • 本文由 发表于 2023年7月10日 23:24:03
  • 转载请务必保留本文链接:https://go.coder-hub.com/76655192.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定