Use sinon.js to create a "spy object" with spy methods based on a real constructor/prototype

13,372

Solution 1

Starting with Sinon 1.6.0 you can do:

var stub = sinon.createStubInstance(MyClass)

See documentation for sinon.stub in Stub API section or the source.

Solution 2

I found a way to do this:

/** Accept a prototype and return a full stub object */
function stub(obj, target) {
  var cls = (typeof obj == 'function') ? obj.prototype : obj;
  target = target || {};

  Object.getOwnPropertyNames(cls).filter(function(p){
    return typeof cls[p] == 'function';
  }).forEach(function(p) { target[p] = sinon.stub() });

  return cls.__proto__ ? stub(cls.__proto__, target) : target;
};

Use it like this:

var response = stub(http.ServerResponse);
response.getHeader.withArgs('X-Foo').returns('bob');
response.getHeader.withArgs('X-Bar').returns('barry');

console.log(response.getHeader('X-Foo')); // bob
console.log(response.getHeader('X-Bar')); // barry
Share:
13,372
d11wtq
Author by

d11wtq

About me I'm a developer in various languages, but most notably Ruby and PHP, shifting more and more in the Ruby direction every day. I also enjoy playing around with Cocoa on OS X and iOS, but I don't pretend to be an expert. I have a half-written multi-language text editor in it. One day I may finish it, though I'm considering writing an Emacs-inspired terminal-based editor in Ruby instead. Employment Flippa.com Pty. Ltd, Melbourne (scroll down for opportunities) Main technologies: PHP, quickly & iteratively moving to Ruby, Rails, DataMapper. I'm the development manager at Flippa.com and spend a lot of time working on stuff for them in my own time, mostly because I love it. We are currently in a long-term transitional period, migrating our codebase from a bespoke PHP framework to Ruby on Rails. Seamlessly running the two applications side-by-side poses some interesting problems. Our biggest ongoing technical challenges revolve around search infrastructure and a fairly complex and ever-evolving bidding system, along with tight integration with many external services. We spend a large amount of time re-thinking and improving our server infrastructure. We love exploring new technologies, where they actually make good sense as far as our needs are concerned. Job Opportunities If you're based in Australia and looking to work in a fun startup environment (we're about 3 years old) in Melbourne, drop me a note either on Twitter (@d11wtq), or via my github profile and we can arrange an interview if we think you're a good fit. We're always looking for technically-capable junior backend-developers/graduates, experienced backend-developers, and (at the current time) client-side developers. People who work with us love the atmosphere. We work in a lively office, shared with our sibling companies (SitePoint.com, 99designs.com, wavedigital.com.au and learnable.com). We have flexible working hours, a fridge full of beer and a foosball table. At the end of every year we all go away together and celebrate in style. Developers are also given the last 3 days in each month to work on projects of their own choosing. Open Source I have a selection of open source projects up on github (@d11wtq). Flippa also likes to share (@flippa).

Updated on June 05, 2022

Comments

  • d11wtq
    d11wtq almost 2 years

    I'm using sinon.js as a way to stub out dependencies in my Mocha tests. I prefer the 'spy' approach over a classic mock approach, as the introspection of the spy seems clearer and affords more flexibility than the somewhat backward-thinking with classic mock objects.

    That said, I wonder if I'm using it incorrectly when it comes to creating test spies for whole objects. Let's say I have a test dependency that has 4 methods on it and I want to stub each of those methods and make assertions on one or two of them. Currently I'm doing this:

    var spyObj = {
      aMethod: sinon.spy(),
      otherMethod: sinon.spy(),
      whatever: sinon.spy()
    };
    

    Then I just ask things like spyObj.aMethod.calledWith(a, b, c).

    Is there a better way to mock out an entire class than repeating the names of the methods in the test suite itself? It looks like sinon.stub() tries to iterate over all the member of a given object, but that doesn't seem to work as a way to get all methods for most objects in more modern JS runtimes, like V8, unless the object is actually something enumerable. It also tries to monkey patch the actual object, rather than returning an equivalent one, which is somewhat undesirable. I just need an object that conforms to an interface, but behaves like a null object, unless I tell it otherwise.

    It would be good to be able to do something like:

    var spyObject = sinon.spy(MyClass.prototype);
    

    How does one find all methods of a constructor/prototype in Node.js, in order to make a wrapper like the above?

    This is more about stubbing out logic, than testing invocations on lots of methods (which I try to limit to one, or at a push two). For example, things that might do unwanted I/O, or require additional complex fixtures if executed.

  • jcollum
    jcollum about 11 years
    This might be helpful to have in the sinon source.
  • sschoof
    sschoof almost 7 years
    The link getting to the documenation overview page: sinonjs.org/releases/v2.3.4/stubs/#stub-api works currently