Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Stubbing and/or mocking a class in sinon.js?

I've created a database wrapper for my application, shown below. To test it, I obviously would like to replace the actual database library. I could create a new class that mocks the query method and catch all input there, but using sinon.js seems more appropriate, but how would I use it?
Is the mock or stub features of sinon.js what I should be using?

wrapper = (function() {

  function wrapper() {}

  wrapper.db = require("database");

  wrapper.prototype.insertUser = function(doc) {
    return this.db.query("INSERT INTO USERS...");
  };

  return wrapper;

})();
like image 574
Industrial Avatar asked Oct 10 '12 12:10

Industrial


People also ask

What is stubbing Sinon?

What are stubs? Test stubs are functions (spies) with pre-programmed behavior. They support the full test spy API in addition to methods which can be used to alter the stub's behavior. As spies, stubs can be either anonymous, or wrap existing functions.

How do you stub a class in JavaScript?

to stub the myMethod instance method in the YourClass class. We call callsFake with a callback that returns the value we want for the fake method. sinon. stub(YourClass, "myStaticMethod").

How do you mock a function in Sinon?

Mocks allow you to create a fake function that passes or fails depending on your needs. You can ensure it was called with certain arguments, or check how many times it was called. You must call mock() on an object.

How do you mock a variable in Sinon?

var sinon = require('sinon'); var start_end = require('./start_end'); describe("start_end", function(){ before(function () { cb_spy = sinon. spy(); }); afterEach(function () { cb_spy. reset(); }); it("start_pool()", function(done){ // how to make timer variable < 1, so that if(timer < 1) will meet start_end.


2 Answers

First, I'd modify your class definition a bit (uppercase class name and fix db assignment):

var Wrapper = (function() {

  function Wrapper() {
    this.db = require("database");
  }

  Wrapper.prototype.insertUser = function(doc) {
    return this.db.query("INSERT INTO USERS...");
  };

  return Wrapper;

})();

To stub the whole class:

var WrapperStub = sinon.spy(function() {
  return sinon.createStubInstance(Wrapper);
});

sinon.createStubInstance will create an instance of Wrapper where every method is a stub. sinon.spy will allow us to spy the class instantiation.

So you could exercise it like this:

// verify instantiation
var wrapper = new WrapperStub();
expect(WrapperStub).to.have.been.calledWithNew;

// verify method stub
wrapper.insertUser.returns('data');
expect(wrapper.insertUser()).to.equal('data');
expect(wrapper.insertUser).to.have.been.calledOnce;

(assertions use chai and sinon-chai)

I said just "exercise it" because this code snippet is not an actual unit test. Instantiation and method calls will be made by your subject under test.

Now, if you want to mock a dependency injected by require() –such as db = require('database') in your example–, you could try a testing tool like either Jest (but not using sinon) or sinonquire which I created inspired by Jest but to use it with sinon plus your favorite testing tool (mine is mocha). Internally, sinonquire uses the same technique shown above of combining sinon.spyand sinon.createStubInstance to stub a class.

like image 56
Ricardo Stuven Avatar answered Oct 13 '22 07:10

Ricardo Stuven


You can use both for that.

Mock have an expected ordered behavior that, if not followed correctly, is going to give you an error.

A Stub is a similar to a mock, but without the order, so you can call your methods the way you want. In my experience you almost never need a mock.

Both of them will substitute your method for an empty method, or a closure if you pass one. It would be something like this:

stub = sinon.stub(wrapper , 'insertUser ', function () { return true; });

Then you add the expect behavior to check if it did happened.

I like to use Jasmine with Jasmine-Sinon for checking the tests.

like image 32
Cristiano Fontes Avatar answered Oct 13 '22 07:10

Cristiano Fontes