Friday, July 18, 2008

Extend JsMock to Support Custom Argument Matcher

I needed some custom argument matching in my mocks today. I am using JSMock for JavaScript mocking and I find it a bit limited when it comes to argument matching. It mostly satisfies my needs but occasionally I need it to be a bit more flexible, generally speaking. Now, what I introduced is not something that xMock frameworks usually have but JavaScript is a dynamic language and some things are simply different compared to "static" languages (OK, I admit, I've used only NMock so far, but I can't be much wrong here).

To use JSMock you need only jsmock.js. The only change required in the original code is in __delegateMatching method of the ArgumentMatcher, changes are marked red:




__delegateMatching: function(expected, actual) {
if( expected == null ) {
return this.__match( expected, actual );
}
else if( expected.constructor == TypeOf ) {
return this.__match(expected.type, actual.constructor);
}
else if( expected.constructor == Array ) {
return this.__matchArrays(expected, actual);
}
else if( expected.constructor == CustomMatch){
if(!expected.match){
throw new Error("Custom matcher not available");
}
return expected.match(expected.object, actual);
}
else {
return this.__match(expected, actual);
}
}


So that was just one added if clause. All I am saying there is if the expected argument is of CustomMatch then use its custom matcher method instead of original matching logic. There is of course the implementation of CustomMatch that is required:




function CustomMatch(object){
this.object = object;
this.Using = function(customMatcher){
if(typeof(customMatcher) != 'function'){
throw new Error("Can only take constructors");
}
this.match = customMatcher;
return this;
}
}

CustomMatch.Match = function(object){
return new CustomMatch(object);
}


Usage is simple, as one of the expected arguments use something like the following construct:
CustomMatch.Match(expectedObject).Using(customMatchFunction)
Where expectedObject is obviously object that you want to match to the actual argument and the customMatchFunction is the function to use for custom matching, it expects two arguments and returns boolean. So usage might look something like this:


var expectedObject = { fieldOne : 1, fieldTwo : 2 };
var customMatchFunction(expected, actual){
return expected.fieldOne == actual.fieldOne && expected.fieldTwo == actual.fieldTwo;
};

someMock.expects().someMethod(CustomMatch.Match(expectedObject).Using(customMatchFunction));

In your custom matching function you can basically do whatever you want, you could use closure and ignore expected object, or extend the CustomMatch to support something like this: CustomMatch.Use(function(actual){ return actual.fieldOne == 1 && actual.fieldTwo == 2;});

If you are using object literals or duck typing this kind of mock functionality might come handy.
That is something you don't have in C# or Java, C# 3.0 comes closest to object literals with anonymous types but they are by far of less usability, and they have nothing close to duck typing.
I hope you find this small extension justified and, more importantly, useful.

No comments:

Post a Comment