Unit Testing ORM with ColdBox 3 and MXUnit
I've just started building a new site using Coldfusion 9's ORM capabilities. Being a good developer (OK, so I'm not but I aspire to be!) I created my tests directory and then hit the problem of how do I actually test Persisted Entities using MXUnit in a ColdBox 3 application?
The problems I had were that my Application.cfc in the root extends the coldbox.system.Coldbox, which I didn't want for testing. Also, the model folder (with all my CFCs in) was a sub folder of the application root - at the same level as my "_tests" folder. The other problem I had was test data. My directory structure looks like this (I like to prefix folders that I don't want in production with an underscore):
/ (web root)
/myproject/ (application root)
/myproject/Application.cfc (extends coldbox.system.Coldbox)
/myproject/_tests/ (folder for my MXUnit tests)
/myproject/model/ (this is where my CFCs to test are)
To solve the issues, I created a Application.cfc in the _tests folder which creates mappings to the model folder. I decided that I would uses the namingstrategy orm setting to force ColdFusion to create the database tables prefixed with "TEST_" so that I could hack away at the data without worrying about breaking the actual applications data.
Here is my _tests/Application.cfc
/**
* I am the Application.cfc for the _tests directory
*/
component
{
this.name = "tests_" & Hash( GetDirectoryFromPath( GetCurrentTemplatePath() ) );
this.sessionManagement = true;
this.sessionTimeout = createTimeSpan( 0, 0, 30, 0 );
this.setClientCookies = true;
// this file is in a directory at the same level as my model folder so need a mapping
this.mappings["/model"] = Replace( GetDirectoryFromPath( GetCurrentTemplatePath() ), "\_tests", "" ) & "model/";
// set up ORM
this.datasource = "mydsn";
this.ormenabled = true;
this.ormsettings = {
cfclocation = this.mappings["/model"],
namingstrategy = "TestNamingStrategy",
dbcreate = "dropcreate",
sqlscript = "testdata.sql"
};
public boolean function onRequestStart( string targetpage )
{
// ORMReload();
return true;
}
}
As you can see I specify an sql script to execute which populates from test data. I also point to a TestNamingStrategy.cfc, to create the alternative database table names. Here it is (note that I had to set the access to remote for MXUnit to call those methods):
component implements="cfide.orm.INamingStrategy"
{
/**
* Defines the table name to be used for a specified table name. The specified table name is either
* the table name specified in the mapping or chosen using the entity name.
*/
remote string function getTableName( string tableName )
{
// prefix table names with "TEST_"
return "TEST_" & UCase( arguments.tableName );
}
/**
* Defines the column name to be used for a specified column name. The specified column name is either
* the column name specified in the mapping or chosen using the proeprty name.
*/
remote string function getColumnName( string columnName )
{
// leave as is
return arguments.columnName;
}
}
I then added a RemoteFacade.cfc file so that I can use MXUnit from inside Eclipse.
component extends="mxunit.Framework.RemoteFacade"
{
}
To use this you need to right click on the project in Eclipse, select "properties > MXUnit properties" and enter the URL to the RemoteFacade.cfc. Mine looks like http://localhost/myproject/_tests/RemoteFacade.cfc.
Finally, ColdBox 3 supports MXUnit so I created a BaseTest to define common settings which all my unit tests extend. This BaseTest.cfc extends coldbox.system.testing.BaseTestCase
component extends="coldbox.system.testing.BaseTestCase"
{
public void function setup()
{
// set the path from the *web* root
setAppMapping( "/myproject" );
super.setup();
}
}
That's it, I don't know if this is a good solution or not, but it works for me!


Comment by Aaron Greenlee – June 02, 2010
Also, with ORM, what do you end up testing? Things like "entityLoad" are what they are and other than making sure _something_ loads, it doesn't seem that valuable of a test.
Thanks.
Comment by James Brown – October 10, 2010
I often test the Service Layer, so I'm testing all the components that the service uses. I like to do this sort of integration testing more than testing of individual components in isolation. For example if I'm testing a login, I want to make sure that the user is found in the database, that they are logged in and they have x,y and z permissions, until I log them out. Then I'd test that they have no longer have any permissions.
Comment by John Whish – October 13, 2010
Comment by JP Revel – November 13, 2010
However if I'm working to a really tight deadline I'd write the tests for the services and then when I have more time add in the tests for individual components
Comment by John Whish – November 16, 2010