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.
9 comments:
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.
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!
You might want to have a look at mockme which is very similar: http://johanneslink.net/projects/mockme.jsp
@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>".
@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.
This looks great. After recently discovering the elegance of Mockito for Java, I'm glad you went trough all the effort to create an equivalent in JavaScript.
I'm running into a little problem, though. Unless I'm missing something, it looks like mock() instantiates a constructor function it is given. That's not gonna work if the constructor requires parameters.
Wouldn't it be better to leave the constructor function untouched, and just create a mock with the same methods?
I'm enjoying using JsMockito, but I'm having difficulty spying on jquery. JsMockito identifies it as a function based on its javascript type, but this causes the spy to fail and delegate calls like $.get, $.post, $.getJSON, and $.noop to their appropriate delegates.
I'm wondering if you're aware of a workaround in the current version that allows for proper mocking of jQuery?
Nice work on JsMockito :-)
One issue, though. Is it really supposed to call through to the real object? The following test fails, and illustrates the point:
var array = [1,2];
var mockArray = mock(array);
mockArray.push(3);
expect(array).toEqual([1,2]);
Now, maybe I'm just using it in peculiar ways (fresh in javascript), but to me this seems eh, wrong. It also makes it very hard to compose objects with mocks, as dependencies on mocks fails when not instanciated.
This is a great framework. Thank you.
Out of curiousity how would you verify that a mock function is called with no arguments?
Post a Comment