GermanHome   Home Offer ClasspathSuite   MockMe   ReFit  

MockMe for JavaScript

There are a couple of mock frameworks for JavaScript but none really did what I needed. So I took my testing problems, some inspiration from mockito and a few days off to write MockMe.

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

  1. Download mockme-0.9beta2.js
  2. Download a recent version of prototype
  3. Include both files like you do with other JavaScript libraries
  4. 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

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 VersionOld 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

License

MockMe is published under LGPL version 3. The concrete license might change in the future, but MockMe will remain freely available.