Unit Test like a Secret Agent with Sinon.js
describe("getTweets", function () {
var fakeData = [
{
created_at: "Fri Apr 05 19:39:30 +0000 2013",
text: "tweet 1",
retweeted: false,
favorited: false,
user: { name: "name 1" }
},
/* ... */
];
before(function () {
sinon.stub($, "ajax").yieldsTo("success", fakeData);
});
it("should $.ajax & invoke callback", function (done) {
twitter.getTweets("elijahmanor", function (tweets) {
expect(tweets.length).to.be(5);
done();
});
});
after(function () { $.ajax.restore(); });
});
> missionImpossible.assignment("accept", tape);
Mission {}
> missionImpossible.numberOfAssignments
1
> missionImpossible.assignment("reject", tape);
⊗ Disintegrate
var missionImpossible = {
numberOfAssignments: 0,
assignment: function (answer, tape) {
var mission = tape(answer);
this.numberOfAssignments++;
return mission;
}
};
function Mission() { }
var tape = sinon.stub();
tape.withArgs("accept").returns(new Mission());
tape.withArgs("reject").throws("Disintegrate");
var stub = sinon.stub(),
opts = { call: function (msg) { console.log(msg); } };
// We can control how the sinon.stub() will behave based on how it’s called!
stub.withArgs("Hello").returns("World");
stub.withArgs("Wuz").returns("Zup?");
stub.withArgs("Kapow").throws();
stub.withArgs(opts).yieldsTo("call", ["Howdy"]);
stub("Hello"); // "World"
stub(options); // "Howdy"
/* ... more ... */
> ethanHunt.called
true
> ethanHunt.calledOnce
true
> ethanHunt.callCount
1
var missionImpossible = {
start: function (agent) {
agent.apply(this);
}
};
// By using a sinon.spy(), it allows us to track how the function is used
var ethanHunt = sinon.spy();
missionImpossible.start(ethanHunt);
var callback = sinon.spy();
callback(); // Invoke the spy callback function
callback.called;
callback.callCount;
callback.calledWith(arg1);
callback.threw();
callback.returned(obj);
callback.calledBefore(spy);
callback.calledAfter(spy);
/* ... more ... */
sinon.spy($, "ajax");
$.ajax({ / ... / }); // Call spy version of jQuery.ajax
var call = $.ajax.getCall(0);
call.args;
call.exception;
call.returnValue;
$.ajax.restore();
describe("getTweets", function () {
var mock, fakeData = [];
before(function () {
mock = sinon.mock(jQuery).expects("ajax").once()
.yieldsTo("success", fakeData);
});
it("should call jQuery.ajax", function (done) {
twitter.getTweets("elijahmanor", function (tweets) {
mock.verify();
done();
});
});
after(function () { jQuery.ajax.restore(); });
});
var opts = { call: function (msg) { console.log(msg); } },
mock = sinon.mock(opts);
// You state your success criteria upfront
mock.expects("call").once().withExactArgs("Hello World");
/* ... twice, atMost, never, exactly, on, etc ... */
opts.call("Hello World");
mock.verify();
mock.restore();
var clock = sinon.useFakeTimers();
var hidden =
$("<div hidden="">Peekaboo</div>")
.appendTo(document.body).fadeIn("slow");
clock.tick(650); // slow = 600ms
hidden.css("opacity") === 1; // true
clock.restore();
var server = sinon.fakeServer.create();
server.respondWith("GET", "/twitter/api/user.json", [
200,
{"Content-Type": "application/json"},
'[{"id": 0, "tweet": "Hello World"}]'
]);
$.get("/twitter/api/user.json", function (data) {
console.log(data); // [{"id":0,"tweet":"Hello World"}]
});
server.respond();
server.restore();
describe("getTweets - Server", function () {
var server, fakeData = [ /* ... */ ];
before(function () {
// Doesn’t work :( It’s JSONP!
server = sinon.fakeServer.create();
server.respondWith(
"GET",
"https://api.twitter.com/.../elijahmanor.json?count=5",
[200, { "Content-Type": "application/json" }, JSON.stringify(fakeData)]
);
});
it("should $.ajax & invoke callback", function (done) {
twitter.getTweets("elijahmanor", function (tweets) {
expect(tweets.length).to.be(5);
done();
}); server.respond();
});
after(function () { server.restore(); });
});