Given a production module like this:
function ($) {
var
showForm = function () {
$.get(url, function (html) {
$('body').append(html);
});
};
return {
showForm: showForm
};
});
I have written the following unit test:
it("calling showForm appends html to existing page", function () {
var testPage = $('<div><body><span></span></body></div>');
var expectedPage = $('<div><body><span></span><label></label></body></div>');
var htmlToAppend = $('<label></label>');
var callback = function () { return testPage.append(htmlToAppend); }
sinon.stub(presenter, "showForm").yields(testPage);
presenter.showForm(callback);
expect(testPage.html()).to.equal(expectedPage.html());
});
I am trying to test that when presenter.showForm
is called, the returned html is appended successfully appended. Problem is, I'm not usre if it's a valid test as pretty much everything is fake.
I'm toying with the idea of abstracting out the call to $('body').append(html);
to another public function of the module or perhaps even to another module so that
the call presenter.showForm(callback);
can use the real function but I'm not sure?
I hope that all makes sense.
Can someone review and advise please?
-
1\$\begingroup\$ First write a failing test. Then make it pass by writing non-test code only. Then you should be sure you haven't faked everything. \$\endgroup\$abuzittin gillifirca– abuzittin gillifirca2013年07月19日 11:22:48 +00:00Commented Jul 19, 2013 at 11:22
2 Answers 2
I'm not quite sure what you mean by "abstracting out the call...", but given that the "production module" actually looks like that, then I'd say that you have a perfectly valid little test there.
It is good to hear that you really are interested in making sure that your test cases are testing the right things. Unit tests are too often something that programmers write simply because they are forced to do it, and they are therefore happy enough when the tests don't fail - not pausing to consider that it might have passed because the test was incorrect, tested the wrong criteria, or did not in fact test anything at all.
Using fake data is what you normally do at this level of functionality, so you are good there. You might generally want to consider using more than one set of test data, but in this case I would say that it is not necessary.
-
\$\begingroup\$ Oh, and by the way: Good advice from @abuzittin in the comment to your post. Do make sure that your tests actually fails when the result is not the expected. For example by adding some character to htmlToAppend without making the corresponding change to expectedPage. \$\endgroup\$Boise– Boise2013年07月19日 23:49:36 +00:00Commented Jul 19, 2013 at 23:49
-
\$\begingroup\$ This did answer my question, so is the accepted one. However, I decided to go down a slightly different route, which I've posted in case it's helpful to others. \$\endgroup\$davy– davy2013年07月23日 08:48:30 +00:00Commented Jul 23, 2013 at 8:48
I decided to add a callback to showForm
so that I could get the returned html from the inner ajax request like so:
var showForm = function (url, callback) {
$.get(url, function (html) {
$('body').append(html);
if (callback != undefined)
callback(html);
});
};
The test now looks like this:
it("showForm calls jQuery.append", function (done) {
// 1
$.mockjax({
url: '/fake',
dataType: 'html',
responseText: '<label></label>'
});
// 2
var spy = sinon.spy($.fn, "append");
// 3
presenter.showForm('/fake', function (html) {
done();
expect(spy.withArgs('<label></label>').calledOnce).to.be.equal(true);
});
});
I used the jQuery.mockjax.js library to setup a fake call and return fake html
from the ajax request.I then setup a spy on jQuery's append function.
I can then check the spy was called with the html that would be returned from the ajax request. (my thinking is that I don't need to test that jQuery actually append the
data, just that the call was made).
This technique gives me more comfort that I am appending the html returned from my ajax request to the form. I would be even more happy if I could test that the body
is being used as the selector but that's another story...
Hope this is helpful.