3

JavaScript テスト コードを Jest から Mocha に移動しています。Jest の優れた機能の 1 つは、モジュールのスタブを自動的に生成することです。これらのスタブはオリジナルと同じ API を実装していますが、すべての関数は を返しundefinedます。エクスポートされたクラスもスタブ化されます。つまり、元のクラスと同じメソッドをすべて持ちますが、戻り値は未定義です。

// mymodule.js
var MyClass = function() { this.foo = 42; };
MyClass.prototype.getFoo = function() { return this.foo; };
module.exports = {
  myclass: MyClass,
  myfunction: function() { return 42; }
};
// __tests__/mymodule-test.js
var mm = require('../mymodule');  // Jest auto-mocks this module.

describe('test', function() {
  it('functions and constructors are mocked', function() {
    expect(mm.myfunction()).toEqual(undefined);  // function is stubbed
    var mc = new mm.myclass();
    expect(mc.getFoo()).toEqual(undefined);  // fn on prototype is stubbed.
  });
});

さまざまな理由から、MochaJS に移行していますが、このスタブ動作を維持したいと考えています。proxyquire を使用して、モジュールのスタブを挿入できます。しかし、スタブを自分で定義する必要があります。

私が望むのは、Node モジュールを受け取り、モジュールの Jest 自動モック バージョンのようなものを返す関数です。これを行う Jest のコードはmoduleMocker.jsにあります。これを行うための独自のコードをいくつか作成しました (以下に含まれています)。しかし、それはかなりトリッキーで、私が書くべきコードとは思えません。

これを行うための標準ライブラリはありますか?

ここに私が書いたものがあります:

// stubber.js
var U = require('underscore');

function replaceFunctionsWithStubs(rootObj) {
  var replacedObjs = [];  // array of [original, replacement] pairs.

  function previousReplacement(obj) {
    for (var i = 0; i < replacedObjs.length; i++) {
      if (replacedObjs[i][0] == obj) {
        return replacedObjs[i][1];
      }
    }
    return null;
  }

  function replacer(obj) {
    var t = typeof(obj);
    if (t != 'function' && t != 'object') {
      // Simple data.
      return obj;
    }

    // Return previous mock to break circular references.
    var prevRep = previousReplacement(obj);
    if (prevRep) return prevRep;

    if (t == 'function') {
      var f = function() {};
      replacedObjs.push([obj, f]);
      if (!U.isEmpty(obj.prototype)) {
        // This might actually be a class. Need to stub its prototype, too.
        var newPrototype = replacer(obj.prototype);
        for (var k in newPrototype) {
          f.prototype[k] = newPrototype[k];
        }
      }

      // Stub any properties the function might have.
      for (var k in obj) {
        f[k] = replacer(obj[k]);
      }
      return f;
    } else if (typeof(obj) == 'object') {
      // TODO: Do I need to handle arrays differently?
      var newObj = {};
      replacedObjs.push([obj, newObj]);

      for (var k in obj) {
        newObj[k] = replacer(obj[k]);
      }
      return newObj;
    } else {
      return obj;  // string, number, null, undefined, ...
    }
  }

  return replacer(rootObj);
}

module.exports = function(m) {
  return replaceFunctionsWithStubs(m);
};
4

0 に答える 0