thlorenz / proxyquire

🔮 Proxies nodejs require in order to allow overriding dependencies during testing.
MIT License
2.75k stars 100 forks source link

Possible to stub constructors? #63

Closed alexjamesbrown closed 9 years ago

alexjamesbrown commented 9 years ago

For example-

In my 'email.js' I have:

var mandrill = require('mandrill-api/mandrill');
var mandrill_client = new mandrill.Mandrill('API_KEY_HERE');

exports.sendTemplate = function(templateName, to, variables, callback) {

    console.log('mandrill_client === ', mandrill_client)

    var message = {
        //omitted for brevity
    };

    mandrill_client.messages.sendTemplate({
        template_name: templateName,
        template_content: [],
        message: message,
    }, function(result) {
        //... do something
        return callback();
    }, function(err) {
        //...
    });
}

Then, in my test, I want to stub out mandrill, so that when a new mandrill_client is created, it gets my stubbed version, ultimately with a sinon.spy on mandrill_client.messages.sendTemplate

//not sure what mandrillStub should be??

email = proxyquire('../../../app/helpers/email', {
    'mandrill-api/mandrill': mandrillStub
});

Is this possible?

bendrucker commented 9 years ago

Sure -- there's nothing special here. Proxyquire is changing what require itself returns. You're not mutating shared values and so you specifically can stub constructors and other primitives that you cannot mutate through a shared reference between your source and test code.

Here's a rough start to get you going:

function MockMandrill (key) {
  this.messages = {
    sendTemplate: sinon.stub()
  }
}

var mandrillStub = {Mandrill: MockMandrill}
alexjamesbrown commented 9 years ago

brilliant, thank you! I'd got myself in a bit of a muddle!

bendrucker commented 9 years ago

You're quite welcome! What I mean by the above comments is that often times you may be able to do something like this:

// ./source.js
var foo = require('foo')
return {
  simple: function (baz) {
    return foo.bar(baz)
  },
  hard: function () {
    return foo()
  }
}
// ./test.js
var foo = require('foo')
sinon.spy(foo, 'bar')

For testing simple you don't actually need proxyquire, though explicitly providing module stubs might be a good idea. Whereas if you needed to change foo itself (i.e. for hard), you can't accomplish that without proxyquire.

neilghosh commented 6 years ago

I am getting "TypeError: Datastore is not a constructor" during the constructor call. I am trying to test the following piece of code.


const Datastore = require('@google-cloud/datastore');

// Creates a client
const datastore = new Datastore({
  projectId: serviceConfig.projectId
});

My test file contains


  function MockDatastore (config) {
    this.projectId = config.projectId;
  }

  var datastoreStub = {Datastore:MockDatastore}

  return proxyquire('../../../app/persistence', {
    '@google-cloud/datastore': datastoreStub
  });
phrozen commented 4 years ago

I'm having the same problem as @neilghosh How do I stub a class constructor and methods?

sostenesapollo commented 1 year ago

Me too, do someone else have the solution ?