jhnns / rewire

Easy monkey-patching for node.js unit tests
MIT License
3.08k stars 128 forks source link

Usage with mocha #39

Closed ColCh closed 9 years ago

ColCh commented 9 years ago

I have small test case... I want to replace some methon on global variable which is Sequelize model:

var models  = ... // sequelize models
var Product = models.Product;
    describe('read', function () {
      var modelsActions = rewire(projectPath + 'lib/excel/modelsActions.js');

      var products = [{name: "Some product from DB"}];
      var fakeFindAll = sinon.stub().returns(Promise.resolve(products));

      var revertRewire;

      before(function () {
        revertRewire = modelsActions.__set__('Product.findAll', fakeFindAll);
      });

      after(function () {
        revertRewire();
      });

      it('should load all products from DB', function () {
        modelsActions.read();
        fakeFindAll.should.be.called;
      });

      it('should order by ascending ID', function () {
        modelsActions.read();
        fakeFindAll.should.be.calledWith(sinon.match({order: 'id ASC'}));
      });

      it('should load without limit', function () {
        modelsActions.read();
        fakeFindAll.should.be.calledWith(sinon.match({limit: 0}));
      });
    });

But it throws:

    main functional
      read
        ✓ should load all products from DB
        ✓ should order by ascending ID
        ✓ should load without limit
        1) "after all" hook

  4 passing (314ms)
  9 pending
  1 failing

  1) Model Actions tests main functional read "after all" hook:
     TypeError: Cannot read property 'findAll' of undefined
      at Object.eval (eval at __set__ (/srv/lib/excel/modelsActions.js:273:19), <anonymous>:1:40)
      at Object.__set__ (/srv/lib/excel/modelsActions.js:273:5)
      at /srv/lib/excel/modelsActions.js:276:24
      at Context.<anonymous> (/srv/test/lib/excel/modelsActions.js:149:9)
      at callFn (/usr/local/lib/node_modules/mocha/lib/runnable.js:251:21)
      at Hook.Runnable.run (/usr/local/lib/node_modules/mocha/lib/runnable.js:244:7)
      at next (/usr/local/lib/node_modules/mocha/lib/runner.js:259:10)
      at Immediate._onImmediate (/usr/local/lib/node_modules/mocha/lib/runner.js:276:5)
      at processImmediate [as _immediateCallback] (timers.js:349:17)

If I get variable and then set it back, everything works like expected.

      var oldProductFindAll;

      before(function () {
        var oldProductFindAll = modelsActions.__get__('Product.findAll');
        modelsActions.__set__('Product.findAll', fakeFindAll);
      });

      after(function () {
        modelsActions.__set__('Product.findAll', oldProductFindAll);
      });

It's a bug or I'm using it wrong?

ColCh commented 9 years ago

Any ideas how revert is now working?

Hm, wrote small workaroud for a while:

var rewireReplace = function (module, replaces) {
  var reverts = {};

  Object.keys(replaces).forEach(function (key) {
    reverts[key] = module.__get__(key);
    module.__set__(key, replaces[key]);
  });

  return function revert () {
    Object.keys(reverts).forEach(function (key) {
      module.__set__(key, reverts[key]);
    });
  };
};

Usage:

      var revertRewire;

      before(function () {
        revertRewire = rewireReplace(modelsActions, {
          'Product.findAll': fakeFindAll
        });
      });

      after(function () {
        revertRewire();
      });
jhnns commented 9 years ago

Should be fixed with 2.1.5 :grinning:

ColCh commented 9 years ago

Thanks.

Partially :)

Works:

      var revertRewire;

      before(function () {
        revertRewire = modelsActions.__set__('Product.findAll', fakeFindAll);
      });

      after(function () {
        revertRewire();
      });

Also this way it works too (I think it's a nice way to inject mocks):

      var revertRewire;

      before(function () {
        revertRewire = modelsActions.__set__({
          Product: {
            findAll: fakeFindAll
          }
        });
      });

      after(function () {
        revertRewire();
      });

And this - nope:

      var revertRewire;

      before(function () {
        revertRewire = modelsActions.__set__({
          'Product.findAll': fakeFindAll
        });
      });

      after(function () {
        revertRewire();
      });

So, I really like this way

        revertRewire = modelsActions.__set__({
          Product: {
            findAll: fakeFindAll
          }
jhnns commented 9 years ago
        revertRewire = modelsActions.__set__({
          'Product.findAll': fakeFindAll
        });

This syntax was never intended to work :grin:. But it is possible to implement, so I added it with v2.2.0

ColCh commented 9 years ago

Thank you! :+1: