【问题标题】:Mocking a dependency in Node在 Node 中模拟依赖项
【发布时间】:2012-03-20 03:08:39
【问题描述】:

我正在学习 Node.js,我想知道人们在单元测试时如何在他们的模块中模拟依赖项。

例如: 我有一个抽象我的 MongoDB 调用的模块。使用这个模块的模块可能会像这样开始。

var myMongo = require("MyMongoModule");
// insert rest of the module here.

我想确保单独测试这样的模块,同时确保我的测试不会将记录/文档插入 Mongo。

是否有我可以使用代理 require() 的模块/包,以便我可以注入自己的模拟?其他人通常如何解决这个问题?

【问题讨论】:

    标签: unit-testing node.js mocking


    【解决方案1】:

    你可以使用像nCore这样的依赖注入库

    说实话,这其中最难的部分实际上是模拟 mongoDB API,它既复杂又不平凡。我估计要模拟出我使用的大部分 mongo API 大约需要一周时间,所以我只是在我的机器上测试本地 mongodb 数据库(它总是处于奇怪的状态)

    然后使用 nCore 特定语法

    // myModule.js
    module.exports = {
      myMethod: function () { 
        this.mongo.doStuff(...)
      },
      expose: ["myMethod"]
    };
    
    // test-myModule.js
    var module = require("myModule")
    
    module.mongo = mongoMock
    assert(module.myMethod() === ...)
    

    【讨论】:

    • 我同意模拟 Mongo 的 API 过于广泛。我以它为例,因为有些人倾向于在他们的数据存储周围应用代理或外观,我试图在其中模拟该代理模块。我肯定会研究 nCore。我还偶然发现了 [Horaa][1],它看起来也很有趣。 [1]:github.com/arunoda/horaa
    • @JamesEggers horaa 丑得要命。它所做的只是var os = require("os"); os.overWriteShit = function () { ... }
    • 好吧,看了nCore和Horaa之后,我同意你的观点,Horaa绝对是丑陋的。我遇到了 Sandboxed-Module,它非常适合我正在寻找的东西。不过,我也很想知道您对此的看法。
    【解决方案2】:

    在查看了 Ryanos 的建议以及 npm 上的 Horaa 包后,我在 Google Group 上发现了这个帖子,它把我指向了 Sandboxed-Module.

    Sandboxed-Module 允许我注入/覆盖 require(),而无需为我的单元测试公开此类依赖项。

    我还在接受其他建议;但是,目前 Sandboxed-Module 似乎满足了我的需求。

    【讨论】:

    • 我个人发现逐个文件覆盖 require 的方法是一种“丑陋的黑客”,并且更喜欢使用适当的 DI。然而,它最适合在现有代码库上模拟一个或两个模块,而无需重写代码以支持 DI。
    • @Raynos Node 的模块系统(正如我所看到的通常使用的那样)并没有真正迎合构造函数注入的想法,这是困难的部分。虽然模块可以是类实例化和注入的简单命名空间,但我在 Node 代码示例中还没有看到很多这种模式。依赖项的属性注入极大地打开了一个模块,以便以意想不到的方式进行修补,这可能会变成像 Rails 一年前升级时遇到的问题。
    • 对 nCore 的一般设计反馈将不胜感激,我不知道对此的最佳解决方案是什么,但我认为拦截 require 不是
    【解决方案3】:

    您可以使用“a”轻松模拟 require:https://npmjs.org/package/a

    例如需要在单元测试中模拟 require('./foo'):
    var fakeFoo = {};
    var expectRequire = require('a').expectRequire;
    expectRequire('./foo).return(fakeFoo);

    //总之:
    var foo = require('./foo); //返回fakeFoo

    【讨论】:

      【解决方案4】:

      覆盖require 以注入您的模拟是一种可能的解决方案。不过,我同意 Raynos 的观点:

      我个人认为逐个文件覆盖 require 的方法是“丑陋的 hack”,我更喜欢使用适当的 DI。然而,它最适合在现有代码库上模拟一个或两个模块,而无需重写代码以支持 DI。

      使用正确的依赖注入不仅可以为您节省“丑陋的黑客攻击”,而且还允许您应用除注入模拟之外的其他用例。在生产中,您可以例如通常通过 http 实例化连接,并在某些情况下注入不同的实现以通过 VPN 建立连接。

      如果您想寻找依赖注入容器,请阅读 excellent article 并查看我实现的 Fire Up!

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-10-13
        • 2019-01-18
        • 2020-12-09
        • 2020-04-14
        • 1970-01-01
        • 2020-03-28
        相关资源
        最近更新 更多