Johannes Link

Software Therapist. Programmer. Supporter.


This is an old project which has not been updated or reconsidered in a long while. Please bear with me!

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

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

  • First public version. I'm trying to collect feedback and see if MockMe is interesting to others at all.

License

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