Welcome!

ColdFusion Authors: Yakov Fain, Pat Romanski, Liz McMillan, Maureen O'Gara, Greg Ness

Related Topics: ColdFusion

ColdFusion: Article

ColdFusion Unit Testing Framework

Paul Kenney's version

Unit testing is receiving a lot of attention these days. Since Macromedia introduced CFCs (ColdFusion Components), developers have been intrigued by the new object-oriented possibilities ColdFusion offers. But OOP also has weaknesses - debugging applications got harder.

Many open source frameworks have already been borrowed from the "mother language," Java. If you examine the coldfusionmx/lib folder you will discover Java libraries like Crimson, jintegra, log4j, Xalan, and others.

cfcUnit is a unit testing framework that is derived from the JUnit framework available for Java. cfcUnit is a part of the OpenXCF project on SourceForge.net. This means that it is available as a true open source project under the Apache license. You can check it out at http://openxcf.sourceforge.net. The author, Paul Kenney, did a great job in porting this application to ColdFusion.

What Is Unit Testing?
The term "unit testing" was not very common in the ColdFusion development community before the release of CFMX. With the introduction of CFCs in ColdFusion MX, developers are looking for better ways to test their applications, and the JUnit example of a unit testing framework has a lot of appeal for CF developers. I will try to explain the goals of unit testing and why it is important for ColdFusion developers.

Unit tests are a key component of software engineering. They are programs written to run in test classes. Each typically sends a class a fixed message and verifies that it returns the predicted answer. Developers write tests for every class they produce. The tests are intended to test every aspect of the class that could conceivably not work. When developers go to release new code, they run all the unit tests. The tests must run at 100%. If any test fails, they figure out why and fix the problem. Of course, sometimes the tests let something slip through. When that happens, developers unconditionally enhance the unit tests so that problem, and any similar one that comes to mind, won't happen again.

Unit testing is especially useful for business logic (not presentation code), which can be tested programmatically rather than requiring user interaction with an interface. Unit testing is also important if you reorganize your code. If you've already written test scripts then it's easier and quicker to test business logic after changes are made (because the test scripts already exist).

Using a testing framework increases the chances that developers will actually write test code. A good testing framework should meet the following expectations:

  • It should be easy to use.
  • It should create tests that can be repeated.
  • It should allow tests to be executed not only from the original author.
  • It should support tests that can be combined and do not interfere with one another.
As I mentioned in the introduction, cfcUnit is a port from the well-known Java unit testing framework JUnit. It is written entirely in ColdFusion Components and based on the API of the JUnit testing framework.

Developers should also understand the limitations of unit testing. Unit testing adversaries argue that it's a waste of time because writing unit tests adds a lot of time to the development process.

In addition, many developers don't like the fact that unit tests never check the interconnections between components within an application.

Installation Steps
Before installing cfcUnit, you must be running CFMX 6.1 and the Mach-II (1.0.9) application framework. Prior versions of CFMX will not work and you should use the latest Mach-II version.

Mach-II is a framework for building robust, maintainable software. You can read more about Mach II on the official Mach II Web site (www.mach-ii.com/) and Sean Corfield's unofficial Mach II Web site (www.corfield.org/machii/).

If you are wondering why Mach-II is required, a Mach-II test runner application is included in the cfcUnit download file. A test runner is an application that provides a framework for performing unit tests. This could be a desktop application, an applet (JUnit contains a swing test runner), or a Web application.

Paul Kenney is working on a non-Mach-II test runner so that the idea of using Mach-II doesn't get in the way of understanding how cfcUnit works.

First you should download the latest version of cfcUnit (v0.8.3) from the cfcUnit Web site - www.cfcunit.org.

Then simply unpack the Zip file into your webroot. A new directory structure "cfcUnit" will be created. cfcUnit should now work for you.

You can try the test runner application by entering http://{your_server_url}/cfcunit/

If you are using the integrated Web server, {your_server_url} could stand for localhost or localhost:8500.

cfcUnit should not be installed on a live production server.

Essential Parts of the Framework
The cfcUnit distribution is made up of two main parts:

  1. The core cfcUnit library components (/org/cfcunit/)
  2. The Mach-II test runner application (/cfcunit/)
The org/cfcunit folder contains the subfolders shown in Figure 1).

Component developers are thinking in objects, so tests are modeled as objects. org\cfcunit\Object.cfc is the base class for the cfcUnit framework. All cfcUnit classes extend object.cfc.

The most important cfcUnit folder is "framework", containing the core files like Assert.cfc and TestCase.cfc.

An assertion is a Boolean expression. I suppose that many readers have already used the custom tag <cf_assert> from Hal Helms (downloadable from www.halhelms.cfm). An exemplary call could look like this:


<cf_assert
	assertion = "
		myAge as a: IsNumeric( |a| ); |a| GT 0; |a| LT 100 >
This assertion would check for "myAge" being numeric - greater then 0 and lower then 100.

org.cfcunit.framework.Assert provides a set of assert methods like "assertTrue()" or "assertEqualsNumber()". Developers can use these asserts in their TestCases.

When an assertion fails, the framework displays the corresponding warning message.

"org.cfcunit.framework.TestCase" is one of the most important classes in the framework. The testing context is commonly referred to as "fixture". Tests in cfcUnit have a common structure - they set up a test fixture (using the method setup()), run testing code against this fixture, collect test results, and clean up the fixture (using the method tearDown()). A test case defines the test fixture to run multiple tests. TestCase contains the abstract methods runTest(), setUp(), and tearDown(). Developers who write tests could extend TestCase and override these functions.

A TestSuite (org.cfcunit.framework.TestSuite) is a composite of tests. It allows us to run many different tests together.

Sample tests to the framework files can be found in the "samples" folder, and tests that test the actual framework are found in the "tests" folder.

The base test runner in the folder "runner" is an abstract base class for all test runners. The test runner files in the folders "htmlui" and "textui" are overriding the abstract base runner class. The Text Runner has limited functionality and only reports test successes or failures. Test results are displayed in plain text (e.g., an error is displayed as "E"). The HTML Runner is used to display much more detailed and useful information.

The "extensions" folder contains useful tools like TestDecorator. The TestDecorator subclasses can add behavior before or after a test is run. After a test is run, TestResult (org.cfcunit.framework.TestResult) collects the results and distinguishes between failures and errors. Failures are anticipated; errors are unanticipated exceptions. cfcUnit checks failures through the assertions mentioned above.

Running the Sample Tests
To run the sample tests, enter one of the following class names into the form field labeled "Test Class":

  • org.cfcunit.samples.AllTests: Runs all tests against the framework files
  • org.cfcunit.samples.ArrayTest: Runs simple array tests
  • org.cfcunit.samples.SimpleTest: Runs "assertTrue", "assertEqualsNumber", and other tests
  • org.cfcunit.samples.money.MoneyTest: Runs tests against the IMoney class and subclasses
Writing Your Own Tests
Now let's write some tests. You should start writing a new TestCase by implementing a subclass of TestCase. This means that you have to create a new CFC. This component must extend the TestCase CFC included with the framework, so we use this attribute in the cfcomponent tag:


extends="org.cfcunit.framework.TestCase"
 
Note: This only works if "org" is directly off the root mapping. If you need to initialize the fixture state, you should override the abstract TestCase function setUp(). You can clean up the fixture by overriding the teardown() method. For example setUp() could be used to open a network connection; the counterpart method teardown() would close this connection. Each test runs in its own fixture, so there should be no side effects among test runs.

Test methods follow the convention that they are public, have a return type of "void", have the prefix "test", and have no arguments. Test methods interact with the fixture. They verify the expected results with assertions.

The framework supports a static type way to run the test. Therefore you have to override the "runTest()" method and define the method to be invoked.

The sample file I wrote uses no static runTest() method. The framework is also capable of running tests dynamically.

To run the sample files, you have to create a folder "cfdjtests" under your webroot. Save the sample files unittest1.cfc and unittest2.cfc (available on the CFDJ Web site) into this folder. Then enter the classnames into the "Test Class" input field and hit the "Run Test" button (see Figure 2).

The first sample (see Listing 1) contains three asserts. The first one is checking for complex values. The second assert is comparing two components and is successful. The third assert will fail, because the assert "assertSameComponent" is comparing two different components - org.cfcunit.samples.money.money and org.cfcunit.samples.money.moneybag. (Code examples for this article can be downloaded from www.sys-con.com/coldfusion/sourcec.cfm.) As you can see in Figure 3, only two asserts are successful.

The second sample (see Listing 2) demonstrates the use of the org.cfcunit.framework.TestSuite component and the addTestSuite() method. This method adds the tests from the given class "cfdjtests.unittest1" to the suite.

Conclusion
cfcUnit is a lightweight framework that makes the process of writing unit testing code easier and more efficient. Unit testing code is a good way to test individual components in isolation from other components. Unit testing can be a great help and can save you time in tracking down and fixing low-level bugs.

Resources

  • cfcUnit: Unit testing framework for ColdFusion: www.cfcunit.org
  • OpenXCF project, http://openxcf.sourceforge.net
  • JUnit.org: Unit testing framework for Java: www.junit.org
  • Paul Kenny's Blog - category cfcUnit: www.pjk.us/paul/blog/index.cfm?mode= cat&catid=7C59BA81-3048-2897-56F676CAFF026A08
  • www.xprogramming.com/software.htm
  • www.mach-ii.com
  • http://cftestingkit.sourceforge.net
  • http://c2.com/cgi/wiki?TestingFramework
  • Beck, Kent. Test-Driven Development: by Example, Addison-Wesley: Boston, 2003: www.amazon.com/exec/obidos/ASIN/0321146530/ objectmentorinc/002-7152366-8413656
  • Link, Johannes. Unit Testing in Java: How tests drive the code. Morgan Kaufmann: San Francisco, 2002. www.amazon.com/exec/obidos/ASIN/1558608680/ objectmentorinc/102-7436013-4738519
  • More Stories By Harry Klein

    Harry Klein is cofounder and CTO at CONTENS Software GmbH, a leading supplier of enterprise content management software. He is a Certified Advanced ColdFusion developer and Microsoft MSCE.

    Comments (1) View Comments

    Share your thoughts on this story.

    Add your comment
    You must be signed in to add a comment. Sign-in | Register

    In accordance with our Comment Policy, we encourage comments that are on topic, relevant and to-the-point. We will remove comments that include profanity, personal attacks, racial slurs, threats of violence, or other inappropriate material that violates our Terms and Conditions, and will block users who make repeated violations. We ask all readers to expect diversity of opinion and to treat one another with dignity and respect.


    Most Recent Comments
    robi 03/21/05 09:50:53 PM EST

    This is a decent article but does not really add anything that you could not get from the website CFCUnit.org nor do the examples seem particularly useful. I would even say that it would be better to read the information on the website and look at the samples in the CFCUnit download if you want to learn how to use the framework. The article does though at least make people aware of this CFCUnit. CFCUnit is a wonderful tool and I would love to see a more sophisticated article discussing how to write more complex test cases and how to integrate unit testing into your development cycle.