如何在Jest中模拟每个测试隐式依赖的模块?

时间:2020-10-26 16:33:06

标签: javascript typescript testing jestjs integration-testing

我有一个集成测试,在该测试中我对MongoDB数据库进行了实际的数据库调用。但是为了测试事务是否过期,我需要为该特定测试模拟数据库。我进行实际数据库调用的原因有很多,仅出于本示例的目的,我提到的是状态。

Jest具有jest.doMock函数,但仅当我想在测试中导入该函数时才有用,但就我而言,这是我想在特定的测试中被调用时为该特定测试模拟的DB函数。表达中间件。

还有一个模拟整个../db模块的选项,但这会使我的实际项目中的测试复杂化很多。如果我可以模拟特定测试的数据库调用,而对其余的所有测试都应该进行真正的数据库调用,这对我来说将非常容易。

Jest有办法吗?

// a.ts
import express from "express"
import db from "../db";

const app = express()

app.get("/api/deduct-balance/:txn_id", (req, res) => {
  const txn = await db.findById(txn_id)
  
  // return error message if txn expired
  if (txn.exipre_at <= new Date()) {
    return res.status(401).json({ error: "txn expired" });
  }

  // otherwise update the txn state
  txn.state = "DEDUCTED";
  await txn.save()

  return res.status(200).json();
});
// a.test.ts
import db from "../db";

describe("mixed tests", () => {
  test("should make REAL db calls", async () => {
    await axios.get("/api/deduct-balance/123")
    const txn = await db.findById("123");
    expect(txn.state).toBe("DEDUCTED");
  });

  test("should use MOCKED value", async () => {
    // need a way to mock the DB call so that I can return an expired transaction
    // when I hit the API

    const { data } = await axios.get("/api/deduct-balance/123")
    
    expect(data).toBe({
      error: {
        message: "txn expired"
      }
    });
  });
})

1 个答案:

答案 0 :(得分:1)

对于这种情况,集成测试是过大的。简单的单元测试就足够了。它们执行起来很快,只测试一件事,所以您应该拥有很多。

由于将处理程序定义为匿名函数,因此默认情况下很难对单元进行测试。因此,第一步是通过提取来简化测试。

// deduct-balance-handlers.ts
export const deductBalanceByTransaction = async (req, res) => {
   const txn = await db.findById(txn_id)

   // return error message if txn expired
   if (txn.exipre_at <= new Date()) {
        return res.status(401).json({ error: "txn expired" });
   }

   // otherwise update the txn state
   txn.state = "DEDUCTED";
   await txn.save()

   return res.status(200).json();
}

这还将使应用程序配置更简洁。

// a.ts
import express from "express"
import db from "../db";
import { deductBalanceByTransaction } from './deduct-balance-handlers';
const app = express()

app.get("/api/deduct-balance/:txn_id", deductBalanceByTransaction);

现在,在不依赖Web框架或数据库的情况下,很容易在测试中重用处理程序。

// a.test.ts
import db from "../db";
import { deductBalanceByTransaction } from './deduct-balance-handlers';

jest.mock('../db');

describe("deduct-balance", () => {
  test("Expired transaction should respond with 401 status", async () => {
    const response = mockResponse();
    deductBalanceByTransaction(request, response);
    expect(response.status).toBe(401);
  });
})

为简单起见,我离开了创建模拟响应和将模块模拟到代码之外的部分。您可以在此处了解更多有关模拟的信息:https://jestjs.io/docs/en/manual-mocks