A First Look at MightyMock
November 27, 2009
MightyMock is a mock framework to help you unit test your applications. I'm using it with MXunit, but you should be able to use it with any Unit Testing framework. So why do you need it?
Well, imagine that you're building an application that has a SecurityService. In your SecurityService you want a method which checks that the current user has a particular permission. OK, let's have a look at what the SecurityService might look like (as with most sample code, this is a bit contrived!)
<cfcomponent output="false">
<!---
create instance struct outside init as dependancy injection frameworks
often call setters before calling init method
--->
<cfset instance = {}>
<cffunction name="init" access="public" output="false" returntype="SecurityService">
<cfscript>
return this;
</cfscript>
</cffunction>
<cffunction name="hasCurrentUserGotPermission" access="public" output="false" returntype="boolean">
<cfargument name="permission" type="string" required="true">
<!--- get the user id --->
<cfset var userid = instance.SessionFacade.getKey("userid")>
<!--- get a User object from the UserGateway --->
<cfset var User = instance.UserGateway.getUserByID(userid)>
<!--- get an array of user's permissions --->
<cfset var userPermissions = User.getPermissions()>
<!--- user is not authorised by default --->
<cfset var authorised = false>
<cfset var index = "">
<cfloop array="#userPermissions#" index="index">
<cfif arguments.permission eq index>
<cfset authorised = true>
<cfbreak>
</cfif>
</cfloop>
<cfreturn authorised>
</cffunction>
<!--- setters to inject dependancies --->
<cffunction name="setSessionFacade" access="public" output="false" returntype="void">
<cfargument name="SessionFacade" type="any" required="true">
<cfset instance.SessionFacade = arguments.SessionFacade>
</cffunction>
<cffunction name="setUserGateway" access="public" output="false" returntype="void">
<cfargument name="UserGateway" type="any" required="true">
<cfset instance.UserGateway = arguments.UserGateway>
</cffunction>
</cfcomponent>
Nothing particuarly unusual going on here, but how do we test it in isolation? When we unit test an object we really just want to test the actual object independantly of any other objects. In this example, the SecurityService depends on a UserGateway and a SessionFacade, also, the UserGateway is going to return a User object. That's three other objects that we need just to test our SecurityService, and the UserGateway calls a database! This is where mock and stub objects come into play.
What a mock framework does is create "fake" objects that stand in for our real objects. In fact, I can test my SecurityService.cfc before I even write any other cfcs and without needing a database!
To use MightyMock we need to download it (I downloaded my copy straight from github) and extract it into the webroot. You'll also need to have MXUnit installed to run my unit test code.
Once you've done that, create the SecurityService.cfc (code above) and the SecurityServiceTest.cfc (code below) in the same directory.
<cfcomponent extends="mxunit.framework.TestCase" output="false" hint="I test the SecurityService">
<!---
to run this test in a browser append ?method=runtestremote to the cfc path
--->
<cffunction name="testUserCanNotDelete">
<cfscript>
// this is the object we want to test
SecurityService = CreateObject('component','SecurityService').init();
/*
our securityservice depends on a SessionFacade and a UserGateway, but
we just want to test the Service independantly, especially as we haven't
even created a SessionFacade or a UserGateway yet! So create mocks.
*/
userid = 123456;
$SessionFacade = CreateObject('component','mightymock.MightyMock').init('SessionFacade');
$UserGateway = CreateObject('component','mightymock.MightyMock').init('UserGateway');
$User = CreateObject('component','mightymock.MightyMock').init('User');
// mock SessionFacade should return userid for getKey('UserID')
$SessionFacade.getKey('UserID').returns(userid);
// give our mock User methods
permissions = ['create','edit'];
$User.getPermissions().returns(permissions);
/*
we want the UserGateway getUserByID method to return our mocked
user object if any numeric value is passed in
*/
$UserGateway.getUserByID('{numeric}').returns($User);
debug($UserGateway.getUserByID(userid).getPermissions());
// inject dependancies
SecurityService.setSessionFacade($SessionFacade);
SecurityService.setUserGateway($UserGateway);
result = SecurityService.hasCurrentUserGotPermission('delete');
assertFalse(result, "The user should not be able to delete");
</cfscript>
</cffunction>
<cffunction name="testUserCanDelete">
<cfscript>
// this is the object we want to test
SecurityService = CreateObject('component','SecurityService').init();
/*
our securityservice depends on a SessionFacade and a UserGateway, but
we just want to test the Service independantly, especially as we haven't
even created a SessionFacade or a UserGateway yet! So create mocks.
*/
userid = 654321;
$SessionFacade = CreateObject('component','mightymock.MightyMock').init('SessionFacade');
$UserGateway = CreateObject('component','mightymock.MightyMock').init('UserGateway');
$User = CreateObject('component','mightymock.MightyMock').init('User');
// mock SessionFacade should return the userid for getKey('UserID')
$SessionFacade.getKey('UserID').returns(userid);
// give our mock User methods
permissions = ['create','edit','delete','restore'];
$User.getPermissions().returns(permissions);
/*
we want the UserGateway getUserByID method to return our mocked
user object if any numeric value is passed in
*/
$UserGateway.getUserByID('{numeric}').returns($User);
debug($UserGateway.getUserByID(userid).getPermissions());
// inject dependancies
SecurityService.setSessionFacade($SessionFacade);
SecurityService.setUserGateway($UserGateway);
result = SecurityService.hasCurrentUserGotPermission('delete');
assertTrue(result, "The user should be able to delete");
</cfscript>
</cffunction>
</cfcomponent>
Then fire it up in a web browser with the url:
http://myservername/mydirectory/SecurityServiceTest.cfc?method=runtestremote
There you go, you've successfully tested that your SecurityService is working correctly without having to create the other cfcs or enter dummy data into your database. Pretty cool huh?
Now I know what you're thinking: "that's a lot of duplicate code for a simple test!". Well, yes it is, thankfully MXUnit has a setup method which is called when each test is run so let's take a detour from the point of this post and refactor...
<cfcomponent extends="mxunit.framework.TestCase" output="false" hint="I test the SecurityService">
<!---
to run this test in a browser append ?method=runtestremote to the cfc path
--->
<cffunction name="setup" hint="I run before each test">
<cfscript>
// this is the object we want to test
SecurityService = CreateObject('component','SecurityService').init();
/*
our securityservice depends on a SessionFacade and a UserGateway, but
we just want to test the Service independantly, especially as we haven't
even created a SessionFacade or a UserGateway yet! So create mock versions
of dependancies. I've prefixed them with a $ but that is up to you.
Passing the object type to the init argument is optional if you are not
using type checking.
*/
$SessionFacade = CreateObject('component','mightymock.MightyMock').init('SessionFacade');
$UserGateway = CreateObject('component','mightymock.MightyMock').init('UserGateway');
$User = CreateObject('component','mightymock.MightyMock').init('User');
userid = 123456;
// mock SessionFacade should return userid for getKey('UserID')
$SessionFacade.getKey('UserID').returns(userid);
/*
we want the UserGateway getUserByID method to return our mocked
user object if any numeric value is passed in
*/
$UserGateway.getUserByID('{numeric}').returns($User);
// inject dependancies
SecurityService.setSessionFacade($SessionFacade);
SecurityService.setUserGateway($UserGateway);
</cfscript>
</cffunction>
<cffunction name="testUserCanNotDelete">
<cfscript>
// give our mock User permissions
permissions = ['create','edit'];
$User.getPermissions().returns(permissions);
// you don't need to debug but it is quite handy
debug($UserGateway.getUserByID(userid).getPermissions());
// test the method
result = SecurityService.hasCurrentUserGotPermission('delete');
assertFalse(result, "The user should not be able to delete");
</cfscript>
</cffunction>
<cffunction name="testUserCanDelete">
<cfscript>
// give our mock User permissions
permissions = ['create','edit','delete','restore'];
$User.getPermissions().returns(permissions);
// you don't need to debug but it is quite handy
debug($UserGateway.getUserByID(userid).getPermissions());
// test the method
result = SecurityService.hasCurrentUserGotPermission('delete');
assertTrue(result, "The user should be able to delete");
</cfscript>
</cffunction>
</cfcomponent>
Once again I must thank Mr Bob Silverberg for his input.
- Posted in:
- ColdFusion
- OOP
- TDD
4 comments
Leave a comment
If you found this post useful, interesting or just plain wrong, let me know - I like feedback :)





Thanks.
Comment by Henry Ho – November 27, 2009
github.com/bobsilverberg/MightyMock/blob/master/MightyMockDoc.pdf
Comment by John Whish – November 30, 2009
Comment by Paul Marcotte – December 02, 2009
Comment by John Whish – December 03, 2009