如何在RequireJS中模拟单元测试的依赖项?

jer*_*son 126 javascript unit-testing mocking requirejs

我有一个我想测试的AMD模块,但我想模拟它的依赖项而不是加载实际的依赖项.我正在使用requirejs,我的模块的代码看起来像这样:

define(['hurp', 'durp'], function(Hurp, Durp) {
  return {
    foo: function () {
      console.log(Hurp.beans)
    },
    bar: function () {
      console.log(Durp.beans)
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

我怎么能模拟出来hurp,durp所以我可以有效地进行单元测试?

And*_*rle 64

所以在阅读这篇文章后,我想出了一个解决方案,它使用requirejs配置函数为你的测试创建一个新的上下文,你可以简单地模拟你的依赖:

var cnt = 0;
function createContext(stubs) {
  cnt++;
  var map = {};

  var i18n = stubs.i18n;
  stubs.i18n = {
    load: sinon.spy(function(name, req, onLoad) {
      onLoad(i18n);
    })
  };

  _.each(stubs, function(value, key) {
    var stubName = 'stub' + key + cnt;

    map[key] = stubName;

    define(stubName, function() {
      return value;
    });
  });

  return require.config({
    context: "context_" + cnt,
    map: {
      "*": map
    },
    baseUrl: 'js/cfe/app/'
  });
}
Run Code Online (Sandbox Code Playgroud)

因此,它创建了一个新的上下文,其中定义HurpDurp将由您传递给函数的对象设置.这个名字的Math.random可能有点脏,但它有效.因为如果您需要进行大量测试,则需要为每个套件创建新的上下文以防止重复使用模拟,或者在需要真正的requirejs模块时加载模拟.

在你的情况下,它看起来像这样:

(function () {

  var stubs =  {
    hurp: 'hurp',
    durp: 'durp'
  };
  var context = createContext(stubs);

  context(['yourModuleName'], function (yourModule) {

    //your normal jasmine test starts here

    describe("yourModuleName", function () {
      it('should log', function(){
         spyOn(console, 'log');
         yourModule.foo();

         expect(console.log).toHasBeenCalledWith('hurp');
      })
    });
  });
})();
Run Code Online (Sandbox Code Playgroud)

所以我在生产中使用这种方法已有一段时间了,它非常强大.

  • +1并不漂亮,但在所有可能的解决方案中,这似乎是最不丑/凌乱的解决方案.这个问题值得更多关注. (13认同)
  • 它只会模仿你传递给`createContext`函数的依赖.因此,在您的情况下,如果您只将`{hurp:'hurp'}`传递给函数,那么`durp`文件将作为普通依赖项加载. (5认同)

bus*_*ted 44

你可能想看看新的Squire.js lib

来自文档:

Squire.js是Require.js用户的依赖注入器,可以轻松实现模拟依赖项!

  • 强力推荐!我正在更新我的代码以使用squire.js,到目前为止我很喜欢它.非常非常简单的代码,引擎盖下没有什么神奇的魔力,但是以(相对)易于理解的方式完成. (2认同)
  • 我在乡绅副作用其他测试方面遇到了很多问题,因此无法推荐它。我会推荐 https://www.npmjs.com/package/requirejs-mock (2认同)

jer*_*son 17

我找到了解决这个问题的三种不同解决方案,其中没有一种令人愉快

定义内联依赖项

define('hurp', [], function () {
  return {
    beans: 'Beans'
  };
});

define('durp', [], function () {
  return {
    beans: 'durp beans'
  };
});

require('hurpdhurp', function () {
  // test hurpdurp in here
});
Run Code Online (Sandbox Code Playgroud)

的fugly.你必须使用大量的AMD样板来混乱你的测试.

从不同路径加载模拟依赖关系

这涉及使用单独的config.js文件来定义指向模拟而不是原始依赖项的每个依赖项的路径.这也很难看,需要创建大量的测试文件和配置文件.

在节点中伪造它

这是我目前的解决方案,但仍然是一个可怕的解决方案.

您可以创建自己的define函数,为模块提供自己的模拟,并将测试放入回调中.然后你eval运行你的测试模块,如下所示:

var fs = require('fs')
  , hurp = {
      beans: 'BEANS'
    }
  , durp = {
      beans: 'durp beans'
    }
  , hurpDurp = fs.readFileSync('path/to/hurpDurp', 'utf8');
  ;



function define(deps, cb) {
  var TestableHurpDurp = cb(hurp, durp);
  // now run tests below on TestableHurpDurp, which is using your
  // passed-in mocks as dependencies.
}

// evaluate the AMD module, running your mocked define function and your tests.
eval(hurpDurp);
Run Code Online (Sandbox Code Playgroud)

这是我的首选解决方案.它看起来有点神奇,但它有一些好处.

  1. 在节点中运行测试,因此不会弄乱浏览器自动化.
  2. 在测试中不需要凌乱的AMD样板.
  3. 你可以eval愤怒地使用,想象一下Crockford会愤怒地爆发.

显然,它仍然有一些缺点.

  1. 由于您在节点中进行测试,因此无法对浏览器事件或DOM操作执行任何操作.只适用于测试逻辑.
  2. 设置仍然有点笨拙.您需要define在每个测试中进行模拟,因为这是您的测试实际运行的地方.

我正在研究一个测试运行器,为这种东西提供更好的语法,但我仍然没有解决问题1的好方法.

结论

在requirejs嘲笑deps很难.我发现了一种有效的方式,但我仍然不满意.如果您有任何更好的想法,请告诉我.


Art*_*rov 15

有一个config.map选项http://requirejs.org/docs/api.html#config-map.

关于如何使用它:

  1. 定义正常模块;
  2. 定义存根模块;
  3. expicitely配置RequireJS;

    requirejs.config({
      map: {
        'source/js': {
          'foo': 'normalModule'
        },
        'source/test': {
          'foo': 'stubModule'
        }
      }
    });
    
    Run Code Online (Sandbox Code Playgroud)

在这种情况下,对于普通代码和测试代码,您可以使用相应的foo实模块引用和存根的模块.


jan*_*ith 9

您可以使用testr.js来模拟依赖项.您可以将testr设置为加载模拟依赖项而不是原始依赖项.以下是一个示例用法:

var fakeDep = function(){
    this.getText = function(){
        return 'Fake Dependancy';
    };
};

var Module1 = testr('module1', {
    'dependancies/dependancy1':fakeDep
});
Run Code Online (Sandbox Code Playgroud)

看看这个:http://cyberasylum.janithw.com/mocking-requirejs-dependencies-for-unit-testing/

  • 我真的希望testr.js能够工作,但它还没有完成任务.最后,我将使用@AndreasKöberle的解决方案,它将为我的测试添加嵌套的上下文(不是很漂亮),但它始终有效.我希望有人能够专注于以更优雅的方式解决这个问题.我会一直看着testr.js,如果/什么时候有效,就会进行切换. (2认同)