A simple JavaScript stubbing function

Stop!

If you are looking for a full featured JavaScript stubbing/mocking library you will be thoroughly disappointed by this post. There are many really good and full featured frameworks for this such as QMock , JSMock and my current personal favorite Sinon.JS.  However, if you want to see a super simple and useful JavaScript stubbing function in less than 20 lines of code you are in luck.  This function (aptly called stub) does all you need to stub out methods on an object.  You can see how many times a method was called and what is was called with.

Here’s the code:

function stub() {
    return {
        of : function (name, callback, returnValue) {
            this[name] = function () {
              var args = Array.prototype.slice.call(arguments);
              this[name].calls.push(args);
              var ret = null;
              if(callback)
                  ret = callback.apply(this, args);
              if(returnValue) return returnValue;
              return ret;
          };
          this[name].calls = [];

          return this;
        }
    };
}

 

Example

To show how to use this stubbing function take a look at the following function and test case which show some code which uses the HTML Canvas context object to draw and translate a blue sqaure.

Test:

test("will render blue square and translate right 50px", function() {
   var context = stub().of("fillRect")
                       .of("translate");

   renderBlueSquareAndTranslate(10,10, context);

   equal(context.fillRect.calls.length, 1);
   equal(context.translate.calls[0][0], 50);
   equal(context.translate.calls[0][1], 0);
   equal(context.fillStyle, "#0000FF");
});

Implementation:

function renderBlueSquareAndTranslate(height,width, context) {
    context.fillStyle = "#0000FF";
    context.translate(50,0);
    context.fillRect(0,0,height,width);
}

 

Tests

Below are the unit tests I wrote for the stubbing function:

  • Stub a method called “action”, assert that it was called twice and assert that the right values were passed to it in each call.
test("will capture call arguments for multiple calls", function() {
   var s = stub().of("action");

   s.action("matt",8);
   s.action("mal",4);

   equal(s.action.calls.length,2);
   equal(s.action.calls[0][0],"matt");
   equal(s.action.calls[0][1],8);
   equal(s.action.calls[1][0],"mal");
   equal(s.action.calls[1][1],4);
});

  • Stub a method called “action”,  assert callback is invoked when “action” is called and use the callback’s return value as “action”’s return value.
test("will invoke callback with arguments", function() {
   var arg0, arg1;
   var callback = function() { arg0 = arguments[0]; arg1 = arguments[1]; return 10;};
   var s = stub().of("action", callback);

   var res = s.action("matt", 8);

   equal(arg0,"matt");
   equal(arg1,8);
   equal(res,10);
});

  • Stub a method called “action” and use given return value as “action”’s return when no callback is specified.
test("will use returnVal over return value of callback", function() {
   var arg0, arg1;
   var callback = function() { arg0 = arguments[0]; arg1 = arguments[1]; return 5;};
   var s = stub().of("action", callback, 10);

   var res = s.action("matt", 8);

   equal(res,10);
});

  • Stub a method called “action” and a method called “other” and assert only “action” is called.
test("will stub multiple methods and test only one is called", function() {
   var s = stub().of("action").of("other");

   s.action();

   equal(s.action.calls.length,1);
   equal(s.other.calls.length,0);
});
 

5 thoughts on “A simple JavaScript stubbing function

  1. You have written a spy object which is able to stub out methods. A better name for this function would be spy, rather than stub. The API to use it doesn’t look very comfortable but thanks for sharing :-)

  2. Thanks for the comment Carlos. I do agree, my use of stub here would be better replaced with the word spy, since the main functionality is that of a test spy.

    As for the API design. I was aiming for a minimalist design (hence only 18 lines of code) but I am curious what you would think would make the interface better. I used this code on a couple small projects and it seemed comfortable to me but I am very biased :)

Comments are closed.