【问题标题】:Most RESTFUL API functions cannot be unit-tested大多数 RESTFUL API 函数无法进行单元测试
【发布时间】:2020-03-12 18:38:58
【问题描述】:
postAdmin(req, res, cb) {
    const admin = await Admin.findOne({ req.body.username });

    if (!admin) {
        error("Admin doesn't exist");
    } //split here? adminValidationDAO();

    const isMatch = await bcrypt.compare(password, user.password);

    if(!isMatch){
        error("Wrong password");
    } //split here? passwordValidationDAO();
    const email = new Email ({
        text: req.body.text,
        title: req.body.title,
    });
    const response = await email.save();
    //split here? saveResponseDAO()?
}

单元测试是测试小段代码的实践, 通常是单独的功能,单独和隔离。如果您的测试使用 一些外部资源,例如网络或数据库,它不是 单元测试。

我正在考虑测试我上面的代码,但在对单元测试进行了一些研究后,我意识到我认为单元测试是集成测试。在 postAdmin 中测试整个代码块将是集成测试。然后我想知道我是否可以将 postAdmin 中的代码分成三块并使它们可单元测试,但这里有以下问题:

第一个使用 findOne() 进行数据库调用。

第二个依赖库,那不是外部资源吗?

第三个使用 save() 调用数据库。

那么你同意没有可以进行单元测试的东西吗?

【问题讨论】:

  • 您可以对其中的大部分内容进行单元测试。只需模拟、存根或以其他方式更改所有外部内容,因此您无需依赖这些内容,然后检查其余逻辑是否正确。
  • 我应该像上面在 cmets 中指出的那样将函数分成三个吗?

标签: node.js unit-testing tdd


【解决方案1】:

您肯定想测试边缘情况,例如查询失败或返回 null 时引发的错误。您应该尝试使用覆盖工具来帮助您确定所有分支(路径)都包含在您的单元测试中。一个伟大且常用的工具是Istanbul。通过这样做,您可以了解测试执行了哪些语句(您应该以 100% 为目标,尽管这有点不可能)。您可以使用模拟框架来模拟外部依赖项并驱动它们的行为,以便您可以专注于您的功能。

例如,我将模拟 Admin.findOne({req.body.username }); 以返回 null,并测试是否引发了错误。

我会为 bcrypt.compare(password, user.password); 做同样的事情并让它返回 false,这样我就可以测试是否引发了错误。 这样做,您将拥有一套测试,允许您重构函数而不必担心改变行为。

【讨论】:

  • 所以您可以对数据库调用进行单元测试,而不是粗体引用中所说的?
  • 我应该像上面在 cmets 中指出的那样将函数分成三个吗?
  • 不,你没有对数据库调用进行单元测试,你正在测试你的情况下的函数行为,当找不到用户名时抛出错误,或者密码不正确,或者一切顺利如果以上都没有发生。我认为您的功能非常清楚它在做什么,我真的不相信您会通过更多地分离它们来增加价值。如果函数变得更大,那么您可能应该考虑将某些部分委托给其他函数。
【解决方案2】:

那么你同意没有可以进行单元测试的东西吗?

不:我看到了逻辑和分支,以及新值的计算。所以这里有可以单独测试的代码。但您可能需要更改设计的其他部分才能实现这一点。

这是 TDD 的一个重要部分 - 用更容易测试的设计代替难以测试的设计。

在这里,您有三个功能(每个功能都很难测试)

admin = f(req.body.username)
isMatch = g(password, user.password)
response = h(req.body.text, req.body.title)

...然后是一堆不关心这三个函数如何实现的逻辑,只关心返回什么值。因此,您可以使用任何您喜欢的这三个元素的替代品来测试其他所有内容。

postAdmin(req, res, cb, f, g, h) {
    const admin = f({ req.body.username });

    if (!admin) {
        error("Admin doesn't exist");
    } //split here? adminValidationDAO();

    const isMatch = await g(password, user.password);

    if(!isMatch){
        error("Wrong password");
    } //split here? passwordValidationDAO();

    const email = new Email ({
        text: req.body.text,
        title: req.body.title,
    });

    const response = h({
        text: req.body.text,
        title: req.body.title,
    });

    //split here? saveResponseDAO()?
}

一旦有了这个想法,您就可以尝试不同的设计来管理依赖关系。

再次,由于逻辑与难以测试的部分分离,您可以将逻辑置于显微镜下并确保一切正常......即使面对使用“真实" 这些依赖项的实现。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-05-22
    • 1970-01-01
    • 2015-07-30
    • 1970-01-01
    • 2019-05-16
    • 2021-01-03
    相关资源
    最近更新 更多