All notes
Sinonjs

Sinon

Standalone test spies, stubs and mocks for JavaScript.

sinonjs.org.

A test example

The following function takes a function as its argument and returns a new function. You can call the resulting function as many times as you want, but the original function will only be called once:


function once(fn) {
    var returnValue, called = false;
    return function () {
        if (!called) {
            called = true;
            returnValue = fn.apply(this, arguments);
        }
        return returnValue;
    };
}

Fake servers

Fake timers

Spies

sinonJS.org: spies.

Creating a spy as an anonymous function



"test should call subscribers on publish": function () {
    var callback = sinon.spy();
    PubSub.subscribe("message", callback);
    PubSub.publishSync("message");
    assertTrue(callback.called);
}

Using a spy to wrap an existing method



{
    setUp: function () {
        sinon.spy(jQuery, "ajax");
    },

    tearDown: function () {
        jQuery.ajax.restore(); // Unwraps the spy
    },

    "test should inspect jQuery.getJSON's usage of jQuery.ajax": function () {
        jQuery.getJSON("/some/resource");

        assert(jQuery.ajax.calledOnce);
        assertEquals("/some/resource", jQuery.ajax.getCall(0).args[0].url);
        assertEquals("json", jQuery.ajax.getCall(0).args[0].dataType);
    }
}

// Ensure it calls the function.
it('calls the original function', function () {
    var callback = sinon.spy();
    var proxy = once(callback);

    proxy();

    assert(callback.called);
});

// The fact that the function was only called once is important:
it('calls the original function only once', function () {
    var callback = sinon.spy();
    var proxy = once(callback);

    proxy();
    proxy();

    assert(callback.calledOnce);
    // ...or:
    // assert.equals(callback.callCount, 1);
});

// We also care about the this value and arguments:
it('calls original function with right this and args', function () {
    var callback = sinon.spy();
    var proxy = once(callback);
    var obj = {};

    proxy.call(obj, 1, 2, 3);

    assert(callback.calledOn(obj));
    assert(callback.calledWith(1, 2, 3));
});

Stubs



it("returns the return value from the original function", function () {
    var callback = sinon.stub().returns(42);
    var proxy = once(callback);

    assert.equals(proxy(), 42);
});

Conveniently, stubs can also be used as spies, e.g. we can query them for their callCount, received args and more.

Sandbox

SO.



//---------- wrap your test function in sinon.test()
it("should automatically restore all mocks stubs and spies", sinon.test(function() {
    this.stub(some, 'method'); // note the use of "this"
    sinon.assert.calledOnce(...);
}));

//---------- manually create and restore the sandbox
var sandbox;
beforeEach(function () {
    sandbox = sinon.sandbox.create();
});

afterEach(function () {
    sandbox.restore();
});

it('should restore all mocks stubs and spies between tests', function() {
    sandbox.stub(some, 'method'); // note the use of "sandbox"
}

//---------- restore()
// Stub readAsDataURL function
let stub = sinon.stub(FileReader.prototype, 'readAsDataURL', function() {
  this.onload({ target: { result: content }});
});

// Trigger event
input.trigger(event);

// We don't want FileReader to be stubbed for all eternity
stub.restore();

QUnit plugin



QUnit.test("Just test", function(assert) {

  var fnMySpy = this.spy(); // Sinon.js functions as part of "this".
  this.stub();
  this.clock.tick(1000);
  this.clock.restore();

  this.sandbox.useFakeServer(); // Stubs are restored automatically after sandbox. wcfTodo: really?

  // Better assert
  sinon.assert.calledOnce(...);
  sinon.assert.calledWith(...);
});

TDD

Seperate UI from processing logic Behaviour oriented, not function oriented. Only those customer-facing functions are needed to be tested, not very function. Setup common rules among the team. Data based check point Unified data model host Team-based shared module (Class, UI control, API...)