Mocking in Javascript

While I do most of my coding in C#, there is still a lot of work I do which resides in the client, namely the browser. While we can employ nice unit testing and mocking frameworks in C#, the same can’t be said for Javascript. There has been a large uptake of Javascript frameworks in these recent years, such as the quite awesome JQuery library. With such a large move into client-side development, it becomes necessary to expand on the tools available for this development platform.

I’ve been introduced to Moq in C#, which I find to be easy to pick up and has a nice syntax. Generally when mocking an interface, we can simply do:

public interface ICalculatorService
{
int Add(int operandA, int operandB);
}
var mock = new Mock();
mock.Setup(c => c.Add(It.IsAny(), It.IsAny())).Returns(100);

int result = mock.Object.Add(1, 2); // Will return 100.
In the above example, I’m simply telling the Moq framework that when the Add method is called on the mocked interface, with any integer for both operands, then return 100. This simple syntax makes writing mocks relatively painless. I wondered if this could also be done in Javascript.

What I set out to do, is create a simple mocking framework in Javascript which behaves similarly to Moq.

As Javascript has no concept of interfaces and contract based programming, we can mimick this ability using a javascript object. If we can imagine this following as a pseudo interface:

var ICalculatorService = {
Add: function() {}
};
To be able to handle the binding betwen the mocked object and it’s associated members, we can create a type called a MockBinding:

var MockBinding = function(name, member, args) {
this.Name = name;
this.Member = member;
this.Arguments = args;
};
MockBinding.prototype = {
Satisfies: function(args) {
if (typeof(this.Arguments) == “undefined”) return true;
if (this.Arguments == null && args == null) return true;
if (this.Arguments == null || args == null) return false;
if (this.Arguments.length != args.length) return false;

for (var i = 0; i < this.Arguments.length; i++) { var argA = this.Arguments[i]; var argB = args[i]; var typeA = typeof(argA); var typeB = typeof(argB); if (typeA == typeB && argA == argB) continue; if (argA == Is.AnyString && typeB == "string") continue; if (argA == Is.AnyNumber && typeB == "number") continue; if (argA == Is.AnyObject && typeB == "object") continue; if (argA == Is.AnyFunction && typeB == "function") continue; if (argA == Is.Undefined && typeB == "undefined") continue; return false; } return true; }
Code language: JavaScript (javascript)

};
With this binding, we can create properties for the name (this is the name of the member), the value of the member, and arguments (where the member is a function). We also add in a method which allows us to compare a set of arguments to see if the current binding would satisfy those bindings. The Satisfies method will firstly check the obvious things, undefined, nulls, array lengths. If that all passes, we can then check the argument types. I’ve defined an enumeration (of sorts):

var Is = {
AnyString: “@any-string”,
AnyNumber: “@any-number”,
AnyObject: “@any-object”,
AnyFunction: “@any-function”,
Undefined: “@undefined”
};
There is no real reason behind the values, they could be anything, numbers, etc.

Next, I defined a utility class for creating the actual mocked object:

var MockUtility = {};
MockUtility._emptyFunction = function() { };
MockUtility.CreateMockedObject = function(graph) {
var object = {};
for (var member in graph) {
var type = typeof(graph[member]);
if (type == “string” || type == “object”) object[member] = null;
else if (type == “number”) object[member] = 0;
else if (type == “function”) object[member] = MockUtility._emptyFunction;
else object[member] = null;
}
return object;
};
The cool thing about javascript objects, is that they are all essentially maps of key-value properties. When we access a propery of an object, we can do so either through dot notation (e.g. c.Add(…)), or through index notation (e.g. c”Add”). We take advantage of this when we create our mocked object. We enumerate over the members in the target object, and create stubs in the mocked object. Strings and objects become null, numbers become 0, functions get set to the empty function property, etc. The result is a copy of the object with stub members, ready to be configured.

Configuration is done using the mock class:

var Mock = function(graph) {
this.Bindings = new Array();
this.Object = MockUtility.CreateMockedObject(graph);
};
Mock.prototype = {
Setup: function(name, member, args) {
var object = this.Object;
var bindings = this.Bindings;

var binding = new MockBinding(name, member, args); bindings.push(binding); if (object[name] != undefined) { if (typeof(object[name]) == "function") { object[name] = function() { for (var i = 0; i < bindings.length; i++) { if (bindings[i].Name == name && bindings[i].Satisfies(arguments)) { return bindings[i].Member.apply(object, arguments); } } } } else { object[name] = binding.Member; } } }
Code language: JavaScript (javascript)

};
When we create an instance of the Mock class, we can use the Mock utility class to create the mocked object and assign it to the Object property of the mock instance. The prototype of the mock class defines the setup method. When we pass a function to the setup method, we create a binding and save any arguments being passed. The function of the object member is not the mocked function itself, but an intermediate function which matches the arguments being passed in to a saved binding. If any other type is passed in, the value is set in the mocked object directly.

Using the above, we can craft a (hopefully readable) mocked example that uses the mocked framework define here:

We define our interface again, and create a mocked instance:

var ICalculatorService = {
Add: function() {}
};
var mock = new Mock(ICalculatorService);
We can then configure the mock. In our example , we’ll make the result of the Add operation return 100 constantly:

mock.Setup(“Add”, function(operandA, operandB) {
return 100;
}, [Is.AnyNumber, Is.AnyNumber]);
Next, we create our consumer class which uses an instance of the ICalculatorService:

var Calculator = function(service) {
this.Service = service;
};
Calculator.prototype = {
Add: function(operandA, operandB) {
return this.Service.Add(operandA, operandB);
}
};
Now, hopefully, when we call the Add operation, it should return 100 regardless, thanks to the mocked object.

var calc = new Calculator(mock.Object);
var result = calc.Add(1, 2);
// result should be 100,
So, hopefully this is a good start to developing a nice Mocking framework, probably to be utilised alongside a Javascript unit testing framework.

The script is attached, let me know what you think of the initial version.
Mock.zip

Leave a Reply

Your email address will not be published.

Related Post