Mocking is something developers use quite regularly when unit testing their code base (duh). Developers love the fact that they can isolate a unit of code from other units and often use mocks to enforce that. I personally think there is too much mocking going on! I also think there are too many unit tests out there and not enough integration tests, which I’ve chosen to define as the following (Java hat is on).
Integration tests
Put simply, tests which stretches over two or more tiers in the application. Tiers may be modules in the code base or a database or search engine. There are no mocks involved in this approach but stubbing if necessary. I wont write anything you don’t already know, so I’ll leave it there.
Unit tests
- As is widely accepted, tests a single unit (class if you will) entirely. This may require the developer to mock out any dependencies (passed either through the constructor (yay) or hacked by making protected methods in your implementation (yuk)). The number of dependencies on the class usually influence the number of mocks in the unit test,
- Integration amongst a few classes that are tightly coupled together. Sometimes there is one entry point into a class which depends on a few more classes. For example, there may be a simple feature which depends on a bunch of XML processing, some transformations and eventually spits out some nice object for you to use. Sure the bits and pieces which make the whole thing work are unit tested in the traditional sense, but the class that provides the entry point should be? I think it should be tested more like an integration test, with each test hitting multiple classes. Its not quite good enough to be labelled an integration test, so I’ve made some wiggle room under the unit test umbrella. In my own mind at least :)
An important point though…
If there is too many dependencies or too much mocking, chances are the class isn’t doing much and just delegates a lot, which means more mocks and more detachment from reality.
Given this statement allow me to make a few points in regards to unit testing your Javascript code! More specifically, unit testing with jQuery.
jQuery is nice, I really like it. I also like testing my code, however its always a pain in the ass when it comes to testing any kind of Javascript! I’ve built (well, building) a testing framework for Javascript which attempts to help test your jQuery, called MimicJS. At this point the jQuery component of the framework needs to be addressed and probably removed, mainly for the reasons I am going to describe here.
If there is one thing I have learnt about Javascript recently it is that mocking jQuery is a bad idea. Those who have done it know the pain, and hopefully they will agree with what I am about to write. Some of you may never have done it before and are going to read all of this and will never mock jQuery. I envy you, you bastard.
Note: the answers (well, my answers) are at the bottom of this post. I decided to list the reasons for not mocking jQuery first, then come up with a solution at the end to bring it all points together.
Mocking jQuery is hard to do
Why bother mocking something that is hard to mock?
The way jQuery is written and works is quite extraordinary. Where most of us are used to the traditional class/method patterns, jQuery disregards all of that and instead uses Javascript to its advantage. Rather than bending Javascript to fit, it works with it. I guess you can look at jQuery as a giant Javascript DSL. Have you ever written an extensive builder in Java, say to create your domain objects? If so, have you ever tried mocking the builder out? Of course not! It may or may not make sense to, but I can almost assure you it would be complicated. So complicated in fact you may be enticed to just use the builder and forget about mocking it altogether…
I’ll now talk about my own experiences whilst creating MimicJS and working with jQuery. The most complicated part by far was attempting to mimic jQuery (you’ll have to have a look at the project yourself to know what im talking about. Do you like my marketing tactics?). Testing behaviour in Javascript is actually quite easy. Most developers who are too lazy to find a behaviour testing framework test the behaviour within the spec itself, it only takes a line or two. So developing mimic for more traditional testing was relatively easy. Testing the behaviour of jQuery was down right painful and also unpolished. If I were to keep that part of MimicJS around I would spend a large chunk of my dev time maintaining it. So for that, I will be removing it soon and replacing with something a little sustainable. You’ll read about it soon.
Mocking jQuery means fragile specs
So what if it is hard to mock jQuery? Some propeller-head will spend endless days and nights building a tool good enough. Well, I can tell you it wont be me :) (And im not admitting im any sort of propeller-head, I’d rather be called a geek). Lets say a tool good enough existed, why wouldn’t I just use it and be done with it?
I’m just making up syntax here, but if a tool good enough to test the behaviour of jQuery existed, this is what it would look like (more or less).
// Implementation function display() { var html = '<span>I am now displayed, and this is the message.</span>'; var fixedHeight = 200; $('#dialog').empty().html(html).show().css('height', fixedHeight); } // Spec it('should display the dialog with a default message provided', function() { display(); $.usingSelector('#dialog').should('empty').and().shouldAlso('show').and().shouldAlso('css', with('height', 200)); });
Now im going to change the implementation. lets say I call height() instead of setting the height using css(), my test is going to break.
// Implementation function display() { var html = '<span>I am now displayed, and this is the message.</span>'; var fixedHeight = 200; $('#dialog').empty().html(html).show().height(fixedHeight); }
Seriously obvious right? Its a simple change, however it doesn’t change the behaviour of the what “really” happens, only the behaviour of the call. This is a single isolated example, but in a large code base you do not want to be making these little changes. Some may argue against that, saying that the call has changed so the tests should fail. I disagree. Like I said the behaviour of what “really” happens has not changed. I’m not interested in maintaining unit tests to that granularity that give me no real benefit. If what “really” happens changes, then and only then should the tests fail. “really” “really” “really”. And don’t forget about BDD. Unless you have a spec that “should fail when I change the way the height is set on an element”, then these specs shouldn’t fail for trivial reasons.
And what about an upgrade to jQuery? You’ll upgrade and any serious breakages are going to be mixed in with useless ones, all of which need to be resolved. Wouldn’t you rather specs fail for good reasons only?
More to the point, if the spec was some kind of integration test it would never fail over small changes like the one shown…
Mocking jQuery means zero DOM interactions
Easily forgotten, mostly because everyone hates it but there is also a DOM involved in all of this. In fact most developers who say they hate Javascript actually hate the DOM. Given that, it sounds like a good idea to avoid the DOM at all costs yeah?
Not really. Whether developers like it or not the DOM is an integral part of the browser and your Javascript code base. Avoiding it while testing is simply going to make the pendulum swing the other way. You’ll end up with specs which mean almost nothing.
Check out this example. Its obvious that there is a selection of an element being made and the wrap function is being called.
// Implementation function highlightingARow() { $('.row:first').wrap('<div class="highlight" />'); } // Spec it('should highlight the first row', function() { highlightingARow(); $.usingSelector('.row:first').should('wrap'); });
The important part in all of this is not that the wrap function is called, but that the element selected actually has the right element wrapped around it. I’ve chosen an example that would require (I guess) some difficulty to implement across multiple browsers. I’ve seen Internet Explorer do some wonderful things with the wrap function, to the point where it had to be implemented differently altogether. With a mocked out jQuery, this spec would never had failed!
Because jQuery has been told to do something doesn’t mean it has done it properly. It’s much better to assert against what actually happened, which means checking the DOM after the jQuery call of course.
And, once again, upgrading jQuery? How confident would you be with replacing an old version of jQuery with a new one, running all your tests with a mocked out jQuery and seeing all your tests pass? Like I said before, jQuery is a giant Javascript DSL. More specifically, a DSL for the DOM.
Mocking jQuery means no TDD
Developers love to boast the fact that they follow many of the Extreme Programming (XP) practices like Pair Programming, Continuous Integration and Test Driven Development (TDD). Not surprisingly, for various reasons its hard(er) to achieve some of these practices in Javascript land. What I want to focus on here is TDD.
Once again, here is the wrap example
// Implementation function highlightingARow() { $('.row:first').wrap('<div class="highlight" />'); } // Spec it('should highlight the first row', function() { highlightingARow(); $.usingSelector('.row:first').should('wrap'); });
Its easy to see that what’s specified in the test is very close to what is described in the implementation. Let me ask this question. Are you going to write the implementation first, refresh your server (if you have to, this is where I have much love for rails) and jump on the browser and verify what you have written is correct? or are you going to write the spec first, then do all those things? jQuery involves plenty of tinkering and will take a few goes to get it right. It would take a jQuery genius to get any benefit out of writing tests first, at least when mocking jQuery is involved. Therefore it would be more beneficial to write a spec which describes what the DOM should look like once the code has been executed, then you can tinker until the spec passes no?
Mocking jQuery is …
I only thought of four things, and instead of changing the heading of the post to “Four reasons to avoid mocking jQuery” I decided to keep it at five because it is a round enough number. That and I’m a bit of a bullshit artist. If I think of a fifth one I’ll post it I swear. A friend of mine put it perfectly, I’m simply TDD’ing (Title Driven Development, no wiki page for that one sorry).
What’s the answer?
It comes back to what I spoke about originally in regards to integration tests, unit tests, and the other use of mocks. Some of us decided that the best what to unit test our Javascript is to mock out all dependencies, whether its dependencies we created or other API’s like jQuery. If we are dealing with unit tests then MAYBE mocking out jQuery is a good thing. But I’m not that big a fan of unit tests when it doesn’t make sense. I can assure you there is minimal gain in mocking out jQuery. If you are testing code that requires DOM interaction in anyway, then it should be classified as an integration test and interact with the DOM as expected.
Now that I am not mocking jQuery any more, here is the same wrap() example from earlier but re-jigged slightly, hopefully highlighting the benefits of taking such an approach. The syntax I am using is similar to what will be implemented in MimicJS soon enough.
// Implementation function highlightingARow() { $('.row:first').wrap('<div class="highlight" />'); } // Spec it('should highlight the first row', function() { $('body').empty().append('<div class="row"/>').append('<div class="row"/>'); when. highlightingARow(); then. expect($('row:first')).toHaveParent($('.highlight')); });
As you can see I have a slightly more verbose test. I have used jQuery itself to set up the test data and have got my expectations based on the state of the DOM and not the behaviour of jQuery. Let’s revisit the four points above and see the benefits.
Mocking jQuery is hard to do. Not much to add here as there is no mocking going on. I can assure you though its much easier to add matchers such as ‘toHaveParent’ than it is to mock jQuery nicely.
Mocking jQuery means fragile tests. If I change my implementation to use a different framework or the name of the wrap function happens to change, then from what I can see my spec is still going to pass. Provided the first row has the parent highlight I dont really care how it is implemented, as long as the state of the DOM at the end is correct.
Mocking jQuery means zero DOM interactions. The issues here have been squashed completely. Because there is no mocking going on means we are interacting with the DOM, which is a good thing. We can upgrade jQuery and be confident the highlight feature hasn’t change its behaviour. We also have more flexibility when it comes to testing across multiple browsers. If the wrap function is broken in IE for example then there will be specs failing for IE and not other browsers. “reality” is tested.
Mocking jQuery means no TDD. It’s obvious when we are mocking out jQuery line for line that there is next to no TDD’ing going on. A trivial example like this its nice to be able to implement test driven, even if only to make yourself feel less dodgy when writing it. Going test driven can be tough for developers, no need to make it tougher by mocking out something as tightly coupled as jQuery.

