Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/javascript/369.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript 如何在Jest中模拟每个测试隐式依赖的模块?_Javascript_Typescript_Testing_Jestjs_Integration Testing - Fatal编程技术网

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

Javascript 如何在Jest中模拟每个测试隐式依赖的模块?,javascript,typescript,testing,jestjs,integration-testing,Javascript,Typescript,Testing,Jestjs,Integration Testing,我有一个集成测试,在那里我对MongoDB数据库进行实际的DB调用。但是为了测试事务是否过期,我需要针对特定测试模拟数据库。我有很多理由进行实际的DB调用,我只是为了这个例子而提到状态 Jest有Jest.doMock函数,但这只有在我想在测试中导入函数时才有用,但在我的情况下,当在express中间件中被调用时,我想为特定测试模拟DB函数 还有另一个选项可以模拟整个。/db模块,但这将使实际项目中的测试复杂化很多。如果我可以为一个特定的测试模拟DB调用,对于其余的所有测试,它应该进行真正的DB

我有一个集成测试,在那里我对MongoDB数据库进行实际的DB调用。但是为了测试事务是否过期,我需要针对特定测试模拟数据库。我有很多理由进行实际的DB调用,我只是为了这个例子而提到状态

Jest有
Jest.doMock
函数,但这只有在我想在测试中导入函数时才有用,但在我的情况下,当在express中间件中被调用时,我想为特定测试模拟DB函数

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

有没有开玩笑的方法

// 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();
});

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

因为您将处理程序定义为匿名函数,所以在默认情况下很难进行单元测试。因此,第一个操作是通过提取它来简化测试

// 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();
}
现在,不依赖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);
  });
})

为了简单起见,我将创建模拟响应和模拟代码中的模块的部分留了下来。在这里可以了解更多关于模拟的内容:

这些类型的测试最好不要作为集成测试来完成。如果您想测试请求处理程序的行为,您应该模拟所有依赖项以创建可预测和可重复的测试。模块
db
应该是一个完整的模拟,并且
findById
应该返回一个模拟事务,等等…@Bart那么作为集成测试,我应该执行哪种类型的测试?我很想知道你对此有什么想法。我留下了一个关于一般要点的答案。不幸的是,我得走了,但我可以在以后的某个阶段扩展我的答案,让它更清楚。我真的很欣赏@Bart的答案,但我特意将示例代码片段保留得很小,以使解释更简单。在实际的项目中,它要复杂得多,这就是为什么我需要一个集成测试。您在上面提到的方法,我在我的许多测试中都使用了这种方法,但我这里的主要挑战是,我是否可以模拟仅在单个测试中隐式依赖的模块。如果你能开玩笑地告诉我这是否可能,那将非常有帮助。:)首先我要考虑的是代码的体系结构。它会妨碍测试吗?如果确实如此,您会发现代码的设计存在一些不正确的地方。在您的示例中,处理程序有很多职责,比如与数据库交谈,决定边缘案例。通常,请求处理程序只做一件事来处理服务层对象的输入(请求)和输出(响应)。它不应该做任何其他事情,因为它是HTTP传输层。如果设计简单,那么依赖性就会减少,模拟的处理也会减少。测试所需的解决方案不是一个好的解决方案。如果您在不同的套件/文件中运行测试,模拟一个模块可能会起作用,但我不建议这样做,因为这会让人困惑。您很可能会使测试过于复杂,因为在当前的设计中无法轻松测试代码。好吧,假设我的代码设计不好,我想更改它。您是否有某些开源库作为示例,最好是后端项目?或者其他人的,我可以寻找某种方法。这不是你可以从其他项目中学到的东西。你会错过它为什么设计成这样的意图。这更多的是关于软件工程,以及将事物分解成合理的模块。将责任和依赖性降至最低,并反复思考您的设计。
// 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);
// 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);
  });
})