Saturday, September 12, 2009

JsMockito - Simple & Better Javascript Mocking

I recently put my developer hat back on and spent a few weeks doing some heavy Javascript development on a ThoughtWorks project in our Pune, India office. As well as writing code, I was also encouraging more and better unit testing of the Javascript codebase - but I was constantly frustrated by the lack of a good mocking library that let me do the sort of things I wanted to do (like mock callback functions).

So in my spare time I put together JsMockito, a Javascript mocking library heavily inspired by the awesome Mockito mocking library for Java.

Give it a go and tell me what you think.

http://jsmockito.org

Why drink it?


JsMockito is a JavaScript stub/mock framework heavily inspired by Mockito. To quote the mockito website:
"Mockito is a mocking framework that tastes really good. It lets you write beautiful tests with a clean & simple API. Mockito doesn't give you a hangover because the tests are very readable and they produce clean verification errors."

JsMockito aims to try and reproduce the clean & simple API, with a JavaScript twist. And why not add some variation to your drinking habits?

What do you serve it with?


JsMockito must be served with JsHamcrest. Not only do they go well together, it's essential to avoid a very nasty hangover.

How to drink it?


To use JsMockito with a JavaScript unit test framework, follow the usual installation/configuration instructions for the framework and plug JsMockito into it. If you're integrating with Screw.Unit (and why wouldn't you?) then you just need to make the following calls:

JsHamcrest.Integration.screwunit();
JsMockito.Integration.screwunit();

Once installed, you can verify with interactions:

var mockedObject = mock(Array);

// -- start code under test --
mockedObject.push("one");
// -- end code under test --

verify(mockedObject).push("one");

Or you can stub method calls:

var mockedObject = mock(Array);

when(mockedObject).get(1).thenReturn("hello world");

// -- start code under test --
alert(mockedObject.get(1));

// the following alerts 'true' as get(99) was not stubbed
alert(typeof (mockedObject.get(99)) === 'undefined');
// -- end code under test --

For a JavaScript twist, you can also mock functions:

mockFunc = mockFunction();
when(mockFunc).call(this, anything()).then(function(arg) {
return "foo " + arg;
});

// -- start code under test --
mockFunc("bar");
// -- end code under test --

verify(mockFunc)(anything());

// or if you want to verify the scope it was called with, use:
verify(mockFunc).call(this, anything())


Mockitos are also good for spys


Real super spies don't drink martinis - they go for mockitos. And just like Mockito, JsMockito supports 'spying' on real functions and objects and verifing how they were interacted with.

An example with functions:

realFunc = function(msg) { alert(msg) };
mockFunc = spy(realFunc);

// -- start code under test --
// the following alerts 'hello world'
mockFunc("hello world");
// -- end code under test --

verify(mockFunc).call(anything(), "hello world");

or with objects:

realObj = new Array();
mockedObj = spy(realObj);

when(mockedObj).pop().thenReturn("bar");

// -- start code under test --
mockedObject.push("foo");
alert(realObj.length); // alerts '1'

alert(mockedObject.pop()); // alerts 'bar'
alert(realObj.length); // still alerts '1'
// -- end code under test --

verify(mockedObject).push("foo");
verify(mockedObject).pop();


Who is your bartender?


This variation is served to you by Chris Leishman and friends. Also a big thumbs up and thanks to the Mockito authors for the inspiration!

Also thanks to the JsHamcrest authors, who made this easy.

5 comments:

Aman King said...

So finally the best kept secret is out. :-) I look forward to using this framework in my next project: looks useful and clean. Keep up the good work.

Daniel F. Martins said...

Great stuff you got there, I'm definitely going to add JsMockito to my JavaScript toolbox!

I'm glad to know that one of my little beasties, JsHamcrest, is being used by such a promising framework. Also, thanks a lot for the credits and for your contributions to JsHamcrest.

Keep up the good work!

johannes.link said...

You might want to have a look at mockme which is very similar: http://johanneslink.net/projects/mockme.jsp

Chris Leishman said...

@dmartins No problem. I really don't understand why most mocking libraries roll their own matchers: I'd prefer to contribute any special ones to your set. That said, I haven't needed to write anymore since then :)

Any thoughts on how to make the framework integration even more obvious? I've basically copied your approach, but I still get people asking why they can't call JsMockito methods without prefixing "JsMockito.<method>".

Chris Leishman said...

@johannes Mockme is nice, but I find the syntax a little cluttered. Must say I'd already started JsMockito after I heard of Mockme, otherwise I might have been tempted to send you patches instead!

I like MockMe's support for tracking replaced methods in existing objects (the .within syntax), and automatically restoring. Whilst well written code shouldn't really need that sort of mocking to allow testing, the reality is that a lot does and frameworks like jQuery do encourage calling global scope functions. So I'll be adding something similar to JsMockito.