The Philosophy
There are two blog posts (1, 2) describing my motivation to develop and publish MockMe.First Aspect: JavaScript is different. That's why JavaScript requires a different type of mock framework. Since functions are the basic building block of JavaScript, functions should also be the basic building block for stubbing & mocking. Of course, we also want to mock, objects - or parts of them - and classes. And yes, creating mock objects that conform to a class or an interface - the way you usually use mock objects in Java - should also be possible.
Second Aspect: While using mockito in Java after many years of being a passionate EasyMock advocate, I have learned that Mockito's way to handle verification of calls after the fact - instead of specifying expectations before - suits my style of test-driven development best. Thus, strictly speaking MockMe should be more of a spying framework than a mocking framework.
How to Use It
Installation
- Download mockme-0.9beta2.js
- Download a recent version of prototype
- Include both files like you do with other JavaScript libraries
- Use it! For example in your JavaScript unit tests.
Creating a mocker
A mocker is the object that gives you access to most of MockMe's functionality.var mocker = new Mocker();That's it. You'll see later that often you even don't need to do that.
Mocking a function
If you need a mock function to hand it to some other object, so this:var mocker = new Mocker(); var f = mocker.mockFunction(); //or even simpler: var f = mocker.mock();Mind that
mock() has many uses, it usually tries to figure out what you want.
Stubbing & verifying calls to a function
//default is to return undefined:
assertEqual(undefined, f('in'));
when(f)('in').thenReturn('out');
assertEqual('out', f('in'));
You can stub to throw exceptions:
when(f)('in').thenThrow({name: 'MypersonalException'});
f('in'); // will throw exception
Or to any behaviour you like:
when(f)('hello').thenDo(function(param) {
alert(param);
});
f('hello'); // will open the alert window with text 'hello'
That's easy. Verifying is exactly as easy:
f('aString');
verify(f)('aString'); // Will succeed
verify(f)('anotherString'); // Will throw an exception
Stubbing & verifying compares the structure of objects
when(f)({name: 'hello'}).thenReturn('yeah');
assertEqual('yeah', f({name: 'hello'}));
assertEqual(undefined, f({name: 'hola'}));
Matchers make stubbing & verifying easier
when(f)(any()).thenReturn('yeah');
assertEqual('yeah', f(1));
assertEqual('yeah', f('two'));
assertEqual('yeah', f([1, 2]));
assertEqual('yeah', f({key: 'value'}));
Other matchers you have are isInstanceOf(aConstructor), isOfType(aType),
anyString(), anyNumber(), isSame(aValue) and
contains(anElementOrObject). The last one works for collections and objects.
Verify the number of calls
f(1); f(2); f(1); f('three');
verify(once(), f)(2); //succeeds
verify(once(), f)(1); //fails
verify(times(2), f)(1); //succeeds
verify(atLeast(1), f)(1); //succeeds
verify(atLeastOnce(), f)(2); //succeeds, this is the default
verify(times(3), f)(anyNumber()); //magic!
Matching existing objects
Consider you have an existing global objects:
var MyObject = {
f1: function() {return 1},
f2: function() {return this.f1() + 2}
}
You can either mock it fully like that:
mocker.mock(MyObject); //The magic mock() again
when(MyObject.f1)().thenReturn('whatever');
verify(MyObject.f2)();
or Partially:
mocker.within(MyObject).mock('f1'); //Also supports many function names
when(MyObject.f1)().thenReturn(5);
assertEqual(7, MyObject.f2()); // f2() keeps its original functionality!
And afterwards you want to restore your global object:
MyObject.unmock(); //Restores a single mocked object // Alternatively: mocker.unmockAll(); //Restores all mocks created by this mocker assertEqual(3, MyObject.f2()); // No more mocking
Life without a mocker object
You can live without explicitly creating mocker objects. Let's take the previous example:
var MyObject = {
f1: function() {return 1},
f2: function() {return this.f1() + 2}
}
useMockerFor(function(mocker)) {
mocker.within(MyObject).mock('f1');
when(MyObject.f1)().thenReturn(5);
assertEqual(7, MyObject.f2());
} // Here everything you mocked will automatically be restored
assertEqual(3, MyObject.f2());
Alternatively you can do things like that:
var MyObject = {...}
var YourObject = {...}
var MyClass = function() {...}
MyClass.prototype = {f: function() {}}
mock(MyObject, YourObject, MyClass).andDo(function(mocker)) {
var my = new MyClass();
my.f(1, 2, 3);
verify(MyClass.prototype.f)(1, 2, 3);
} // auto restore
This example also showed you how to mock a class, i.e. a constructor function.
Stub out or spy on classes
Stub out or spy on classes without having to create a mock instance first:
var MyClass = Class.create({ //Class is from PrototypeJS
initialize: function() {}
f: function() {}
});
mocker.mockClass(MyClass);
when(MyClass.prototype.f)().thenReturn('oops');
var myInstance = new MyClass(42);
assertEqual('oops', myInstance.f());
verify(MyClass.prototype.initialize)(42);
Creating mocks for an interface or a class that serves as an interface
Create a mock object which implements all methods from a class:
var MyClass = function() {}
MyClass.prototype = {f: function() {}}
var myMock = mocker.mockInterface(MyClass);
when(myMock.f)().thenReturn('oops');
To make up an interface on the fly:
var MyInterface = Interface.create('f', 'g', 'h'); //Interface is defined by MockMe
var myMock = mocker.mockInterface(MyInterface); //has three functions: f, g & h
Stuff missing in this documentation
Quite a bit of details and hidden features. Enjoy!Open Issues
- Dependency on prototype. This can interfere with other libraries you'd like to use.
- I'm fighting with some terms of MockMe's DSSML (domain specific stubbing & mocking language). Please give me your suggestions!
- Stubbing & verifying calls in a certain order is not supported yet.
Release Notes
Version 0.9 beta 2 2008-08-07
After a discussion with one of the Mockito guys at Agile 2008, I changed the stubbing syntax:| New Version | Old Version |
|---|---|
when(fun)(params). thenReturn(value) |
stub(fun)(params). toReturn(value) |
when(fun)(params). thenReturnNothing() |
stub(fun)(params). toReturnNothing() |
when(fun)(params). thenThrow(exception) |
stub(fun)(params). toThrow(exception) |
when(fun)(params).thenDo(closure) |
stub(fun)(params).toDo(closure) |
Version 0.9 beta 2008-07-29
- First public version. I'm trying to collect feedback and see if MockMe is interesting to others at all.