Some helpers for CFWheels DBMigrate Plugin

Here is a handy controller that I use with my CFWheels application deployments & for debugging my database migrations with Troy Murray’s invaluable CFWheels DBMigrate Plugin.

I use a git branching strategy that utilises feature branches, which can sometimes lead to database migration files being out of sequence based on their creation date.. So I needed a way to test that all current migration are in the correct order… Enter the magical DBMigrate.cfc controller.

To get this up and running, You’ll need to install the CFWheels DBMigrate Plugin, the /models/DBMigrateVersion.cfc model file and the /controllers/DBMigrate.cfc controller from https://github.com/chapmandu/cfwheels-dbmigrate-plugin-tools.

Once these files are in place, you can call the following URLs and see the JSON they return:

http://yoursite/index.cfm?controller=dbmigrate&action=ping

{
db: "okay"
}

http://yoursite/index.cfm?controller=dbmigrate&action=migrations

{
totalMigrationCount: 4,
isOrdered: true,
latest: "20160205201245",
versions: [
{
version: 20140527111730,
cfc: "20140527111730_create_tables",
migrated: true,
details: "create tables"
},
{
version: 20160205201146,
cfc: "20160205201146_insert_lookup_rows",
migrated: false,
details: "insert lookup rows"
},
{
version: 20160205201233,
cfc: "20160205201233_add_plain_text_password_field_to_users_table",
migrated: false,
details: "add plain text password field to users table"
},
{
version: 20160205201245,
cfc: "20160205201245_create_credit_card_number_columns",
migrated: false,
details: "create credit card number columns"
}
],
current: 20140527111730,
migratedCount: 1,
notMigratedCount: 3,
isMigrated: false
}

http://yoursite/index.cfm?controller=dbmigrate&action=current

{
current: 20140527111730
}

http://yoursite/index.cfm?controller=dbmigrate&action=latest

{
latest: 20160205201245
}

http://yoursite/index.cfm?controller=dbmigrate&action=ismigrated

{
ismigrated: false
}

The main one I use for my automated tests is the big sexy action=migrations packet. If any of the migrations are not in the correct sequence, the isordered node in the JSON will have a value of false Here is the RocketUnit test that I run to check if all migration are in the right order (and can be run automatically).

public void function test_db_checks_awaiting_migration_files_should_be_in_order()
{
    loc.args.action = "migrations";
    loc.response = getResponse(argumentCollection=loc.args);
    loc.substring = '"isordered":true'
    assert('loc.response contains loc.substring');
}

Disclaimer: Please be careful leaving this controller lying around in your production environment.. It doesn’t perform any database changes, but can give some basic information about your database migrations to anyone who may stumble upon your URL..

If you use the DBMigrate plugin, you may like this blog post… If you don’t use the plugin.. You really should.

Advertisements

JUnify Plugin for CFWheels

Make the CFWheels test framework return jUnit XML

JUnify will run your CFWheels unit tests, then return the results in jUnit formatted XML. Use it for automated testing with your Continuous Integration server.

Following the road to jUnit Glory

  • Download and install the JUnify plugin from CFWheels or GitHub
  • Create a JUnifyController.cfc controller that calls the JUnify() function*
  • Write some unit tests for your app or plugin
  • Make a call to your JUnify url

* Controller filename and action names are unimportant

JUnifyController.cfc

<cfcomponent extends="Controller">
	
	<cffunction name="index">
		<cfset JUnify(params)>
	</cffunction>
		
</cfcomponent>

Your JUnify URL
http://yourserver/index.cfm?controller=junifycontroller&action=index&type=app

Pay particular attention to the type parameter.
– type=app will run tests for your application
– type=myplugin will run tests for your plugin
– type=core will run tests for the CFWheels framework itself

Props to Rocketboots for their RocketUnit test framework

DatePicker Plugin for CFWheels

Use a jQuery UI datePicker widget in your CFWheels forms

datePicker and datePickerTag functions accept the same arguments as CFWheels textField and textFieldTag functions respectively, but they also accept all options for the jQuery datePicker widget

Following the road to DatePicker Glory

1. Install the DatePicker plugin from CFWheels or GitHub
2. Make sure you have setup jQuery AND jQuery UI in your app (consider my JQuack plugin)
3. Use the datePicker() and datePickerTag() form helpers

Examples

<!--- Use with objects --->
#datePicker(label="Birthday", objectName="user", property="birthday")#

<!--- Use without objects --->
#datePickerTag(label="Birthday", name="birthday", value=Now())#

Note: All jQuery specific arguments (jQuery datePicker options) are case-sensitive

CFWheels Plugin Function Argument Defaults

A handy and powerful CFWheels feature for keeping your code DRY is to be able to configure application-wide default for wheels functions.

It’s also possible to tap into the CFWheels framework to make this feature available to functions added by your own plugins. Here’s how.. (props to Andy Bellenie)

Let’s assume you have created a plugin called putOnWeight and in this plugin, you have a function called eatCake()

As per the CFWheels documentation you set your application-wide function defaults in your settings.cfm like so:

<!--- config/settings.cfm --->
<cfset set(functionName="eatCake", howMany=20)>

To have your plugin accept these function defaults, you will need to tap into the CFWheels $args() function.. like so:

<!--- plugins/PutOnWeight.cfc --->
<cfcomponent output="false" mixin="controller">

	<cffunction name="init">
		<cfset this.version = "1.1.7">
		<cfreturn this>
	</cffunction>

	<cffunction name="eatCake">
		<!--- note that howMany argument is not required, and has no default --->
		<cfargument name="howMany" type="numeric" required="false">
		<!--- use the CFWheels $args function to look for default arguments set in settings.cfm --->
		<cfset $args(name="eatCake", args=arguments)>
		<!--- cake.. nom nom nom! --->
		<cfset var loc = {}>
		<cfset loc.cakesEaten = arguments.howMany>
		<cfreturn loc.cakesEaten>
	</cffunction>

</cfcomponent>

Disclaimer: the $args() function is a private cfwheels framework function so use at your own peril… I am.

JQuack for jQuery CFWheels Plugin

Manage and call your jQuery core files, plugin packages, UI and themes with a set of JQuack functions. Download it here..

It also adds a simple UI for configuring all your core and plugins
This plugin adds a set of functions to call your jQuery core files, plugin packages, UI and themes. You can can also call files from an external library (Eg: Google CDN).

There is also my standalone CFC for use outside of CFWheels (but why would you want to do that?)

Contributions
Feel free to contribute via GitHub or drop me an email.. I have also written a bunch of unit tests if you’re into that sort of thing, they’re all available on GitHub.

Credits
Tony Petruzzi for his many replies to all things CFWheels, plugins and unit testing
CFWheels and jQuery for all-round awesomeness
JQuack Roadmap
Better support for external file requests
Support for non minified versions of UI files
File verification tool
Unit test improvements

Function Syntax
JQuackCore([coreFileName, rootURL, head])
JQuackPlugin([key, file, pluginRootURL, delimiter, head])
JQuackUI([UIBundleName, themeName, rootURL, delimiter, head])
JQuackTheme(themeName, [UIBundleName, rootURL, head])
JQuackAll([delimiter, head])

Recommended Usage Example

<!--- 1. include all files as defined in your JQuack configuration --->
#JQuackAll()#

<!--- 2. include minimal files --->
#JQuackCore()#
#JQuackPlugin("foo,bar")#
#JQuackUI()#

<!--- 3. include a combination of files --->
#JQuackCore()#
#JQuackPlugin("foo,bar")#
#JQuackPlugin(file="jquery.foo.js", pluginRootURL="http://www.foobar.com/plugins/")#
#JQuackUI(themeName="barfoo")#
JQuackCore()
Usage Example
<!--- JQuackCore() Examples --->
<!--- call the core file as defined in config --->
#JQuackCore()#

<!--- write the output to the head area of the HTML page --->
#JQuackCore(head=true)#

<!--- call a different version of the core file from your javascripts/jquery/ folder --->
#JQuackCore(coreFileName="jquery-1.5.0.min.js")#

<!--- call from Google Libraries API --->
#JQuackCore(coreFileName="jquery.min.js", rootURL="http://ajax.googleapis.com/ajax/libs/jquery/1.6.4/")#
JQuackPlugin()
Usage Example
<!--- JQuackPlugin() Examples --->
<!--- call a single plugin package --->
#JQuackPlugin("foo")#

<!--- call multiple plugin packages --->
#JQuackPlugin("foo,bar,quack")#

<!--- don't worry, none of the 'foo' files will be included more than once --->
#JQuackPlugin("foo,bar")#
#JQuackPlugin("foo")#

<!--- write the output to the head area of the HTML page --->
#JQuackPlugin(key="foo,bar", head=true)#

<!--- call all plugins --->
#JQuackPlugin()#

<!--- remove line breaks between included files --->
#JQuackPlugin(delimiter="")#

<!--- call a plugin package from another location --->
#JQuackPlugin(key="foo", pluginRootURL="http://someserver.com/js/plugins/")#
#JQuackPlugin(key="bar", pluginRootURL="/javascripts/someotherfolder/")#

<!--- call a file not defined in your packages but located in the /javascripts/jquery/plugins/ folder --->
#JQuackPlugin(file="jquery.foobar.js")#

<!--- call a file from another location --->
#JQuackPlugin(file="jquery.foobar.js", pluginRootURL="http://someserver.com/js/plugins/")#
JQuackUI()
Usage Example
<!--- JQuackUI() Examples --->
<!--- call the UI bundle as defined in config --->
#JQuackUI()#

<!--- include the UI with a different theme --->
#JQuackUI(themeName="rainy")#

<!--- include a different UI bundle --->
#JQuackUI(UIBundleName="jquery-ui-1.2.34.custom")#

<!--- write the output to the head area of the HTML page --->
#JQuackUI(head=true)#
JQuackTheme()
NOTE: This function is called within JQuackUI(), so should only be used to override the theme used in JQuackUI()

Usage Example
<!--- JQuackTheme() Examples --->

<!--- include a different theme --->
#JQuackTheme(themeName="wacky")#

<!--- write the output to the head area of the HTML page --->
#JQuackTheme(themeName="wacky", head=true)#
JQuackAll()
Usage Example
<!--- JQuackAll() Examples --->
<!--- include all files --->
#JQuackAll()#

<!--- include all files without line breaks --->
#JQuackAll(delimiter="")#

<!--- write all output to the head area of the HTML page --->
#JQuackAll(head=true)#

CFWheels Plugins and My Unit Testing Adventures!

Seeing as unit testing your CFWheels plugins has yet to be thoroughly documented, I thought I would share my experiences with writing a unit test suite for my jQuack plugin. Here is my take on writing well organised unit tests, I am by no means an expert. In fact before embarking on this project, I was a total unit test noob, but I learned valuable lessons from trial, error and much advice from Tony Petruzzi.

Unit tests are a set of repeatable tests that determine if your code meets its requirements. In a way, they also document your plugin’s behaviour as the tests themselves describe its requirements. Unit tests encourage you to write re-useable, modular code and are invaluable for checking that your plugin still function as designed when:

  • A new version of CFWheels is released
  • Testing across CFML engines
  • Expanding or refactoring your plugin

For more information on developing plugins for CFWheels, go here.. I will use my jQuack plugin as the basis for my examples, your plugins will of course need different types of tests.. but that’s why you get paid the big bucks!

But enough jibber jabber.. Let’s get testing fool!


1. Create a folder called “tests” in your plugin’s folder (eg /plugins/jquack/tests).
This is where your tests code, and any assets required for the tests will reside. This assets folder can (and should) mimic your application folder structure, but only create the folders you use in your tests (see the next step for how to use them). For jQuack, I needed an isolated bunch of assets (files, folders & variables) that I could butcher with my tests without affecting my actual application.

2. Create a Base.cfc in the tests folder and be sure to extend the wheels Test component.
This is not required, but I would consider it good practice and will make your test suite scalable. This cfc will contain tasks that are common to all of your tests, primarily for setup and teardown, but it may also contain helper functions that are used by your tests. This particular implementation of the setup and teardown methods are VERY important. At the start of each test, the setup method will be called, which will copy the existing application scope and massage it for this test. The test will then run and the teardown method will revert the application scope back to its original state. NOTE: The setup and teardown happens before and after EVERY test.. not just before and after the request. This isolated assets environment was probably the single most important concept for me, once I got this working, writing the actual tests was a breeze!

<cfcomponent extends="wheelsMapping.Test">

	<cffunction name="setup">
		<!--- save the orginal environment --->
		<cfset loc.orgApp = Duplicate(application) />
		<!--- path to our plugins assets folder where we will store our components and files --->
		<cfset loc.assetsPath = "plugins/JQuack/tests/assets" />
		<!--- repoint the lookup paths wheels uses to our assets directories --->
		<cfset application.wheels.controllerPath = "#loc.assetsPath#/controllers" />
		<cfset application.wheels.pluginPath = "#loc.assetsPath#/plugins">
		<cfset application.wheels.javascriptPath = "#loc.assetsPath#/javascripts" />
		<!--- we're always going to need a controller for our test so we'll just create one --->
		<cfset params = {controller="foo", action="bar"} />
		<cfset JQuackController = controller("jquack", params) />
	</cffunction>
	
	<cffunction name="teardown">
		<!--- recopy the original environment back after each test --->
		<cfset application = loc.orgApp />
	</cffunction>
		
</cfcomponent>

3. Create your unit tests cfc
Be sure to prefix the file with “Test” or it will be skipped. Also make sure to extend your Base.cfc AND call super.setup() and super.teardown() in this cfc. I like to number my tests so they run in the order specified. I also like to give my tests descriptive names. Don’t be afraid to break your tests up into separate packages. I have split mine up into TestData.cfc, TestMarkup.cfc and TestScaffold.cfc. I like to test simple variable names against each other (a eq b) rather than have my assert() methods all messy… see tests 04 and 05.

TestMarkup.cfc

<cfcomponent extends="Base">

	<!--- call the setup & teardown from Base.cfc --->
	<cffunction name="setup">
		<cfset super.setup()>
	</cffunction>
	
	<cffunction name="teardown">
		<cfset super.teardown()>
	</cffunction>
	
	<!--- make sure setup & teardown work --->
	<cffunction name="test_00_setup_and_teardown">
		<cfset assert('true') />
	</cffunction>

	<!--- no need for me to comment what this does.. the function name says it all --->
	<cffunction name="test_01_$renderMarkup_returns_js_markup">
		<cfset file = "test_01.js" />
		<cfset a = JQuackController.$renderMarkup(file) />
		<cfset b = $JSTag(file) />
		<cfset assert('a eq b') />
	</cffunction>

	<!--- same here --->
	<cffunction name="test_02_$renderMarkup_prevents_duplicate_rendering">
		<cfset a = JQuackController.$renderMarkup("test_03.js") />
		<cfset b = JQuackController.$renderMarkup("test_03.js") />
		<cfset assert('Len(b) eq 0', 'b') />
	</cffunction>

	<!--- and here --->
	<cffunction name="test_03_core_method_default_returns_correct_markup">
		<cfset a = JQuackController.JQuackCore() />
		<cfset b = $JSTag("#JQuackController.$JQueryDirectoryURL()##loc.config.coreFileName#") />
		<cfset assert('a eq b') />
	</cffunction>
	
	<!--- messy --->
	<cffunction name="test_04_messy_example">
		<cfset assert('JQuackController.JQuackCore() eq $JSTag("#JQuackController.$JQueryDirectoryURL()##loc.config.coreFileName#")') />
	</cffunction>
	
	<!--- sexy --->
	<cffunction name="test_05_sexy_example">
		<cfset a = JQuackController.JQuackCore() />
		<cfset b = $JSTag("#JQuackController.$JQueryDirectoryURL()##loc.config.coreFileName#") />
		<cfset assert('a eq b') />
	</cffunction>

	<!--- more tests go here --->

	<!--- private helper functions keep things DRY --->	
	<cffunction name="$JSTag" access="private">
		<cfargument name="file" type="string" required="true" />
		<cfreturn '<script type="text/javascript" src="#arguments.file#"></script>' />
	</cffunction>

</cfcomponent>

The above is a snippet only for demo purposes.. the full test suite is available using the links below.
Once you have written your tests, you should run them using the ‘Run Tests’ link next to the name of your plugin in the CFWheels debug area.

4. Gotchas
In order to test any private methods in your plugin, they will need to be made public.. I prefix my “private” (but public) methods with a $ as per the CFWheels framework convention.

<cffunction name="$CRLF" returntype="string" access="public">
	<cfreturn Chr(13) & Chr(10) />
</cffunction>

This plugin uses the mixin=“controller” attribute, I’ve not yet built a plugin with any other value.

These 2 settings are pretty handy..

<!--- In config/design/settings.cfm --->
<cfset set(overwritePlugins=false)>
<cfset set(deletePluginDirectories=false)>

The JQuack plugin and its test suite is available for browsing or download from GitHub or from the CFWheels plugins directory.

Some handy links :
CFWheels Testing Framework
Writing Great Unit Tests
Creating CFWheels Plugins

Please comment if there are any parts that are unclear and I will try to clarify.