Here are a list of testing scenarios. I will fill in a short example of how to use ColdUnit in each situation below.

Setup and teardown

When writing a set of tests for a particular custom tag (for example) you may need to (again for example) insert test data into the database before calling your custom tag, so that the tag has test data to work on, returning predictable results. Instead of placing those <CFQUERY>s into each test script, you can factor them out into a single script that ColdUnit will run before each test in that directory. This works as follows.

Within a given test directory (say testGetEmployees), you have two test scripts, testGetEmployeesAsOfCurrentDate.cfm and testGetEmployeesAboveSalaryCutoff.cfm (I'm envisioning a custom tag that accepts optional parameters specifying an active date and/or a salary cutoff for filtering the employee list). For both of them, you want to delete all the rows in your employee table and insert a few known records, so that your custom tag will always return the same results to your test. So, you create a file called setup.cfm in the same directory. In this script you place the <CFQUERY> tags that perform these data modifications. The ColdUnit framework will then run this script before each of your tests automatically (if you run your tests using allTests.cfm as described in the quickstart). Your directory structure would then look like this:

  /testGetEmployees
    setup.cfm
    testGetEmployeesAsOfCurrentDate.cfm
    testGetEmployeesAboveSalaryCutoff.cfm

You could also provide another file, teardown.cfm, to remove any data that you did not want to leave behind in the database. However, this is unusally unnecessary, because running tests within the ColdUnit framework ensures that each test is run within a separate transaction, and each transaction is rolled back at the end of each test (regardless of what exceptions are thrown within the test). Thus, any DML statements executed during your setup or test affect only that test and nothing else.


Testing a boolean condition

Testing boolean conditions is simple: use the coldunit.assert custom tag provided with the framework:

<CFMODULE template="../isUserAuthorized.cfm" username="bob" password="bobspassword" returnVar="isAuthorized">

<CFMODULE name="coldunit.assert" condition="#isAuthorized#" message="bob was not authorized">

If #isAuthorized# evaluates to true ("Yes", 1, etc.), then the assertion will pass.


Testing case-sensitive string equality

assertEquals accepts an optional parameter, caseSensitive, that allows you to make assertEquals' comparisons case-sensitive. Its default value is "No", so that

<CFMODULE name="coldunit.assertEquals" expected="abc" actual="ABC">

will pass but

<CFMODULE name="coldunit.assertEquals" caseSensitive="Yes" expected="abc" actual="ABC">

will fail.


Testing exceptions

Sometimes you want to test that a particular custom tag will throw a particular exception under certain conditions. The standard way to do this with ColdUnit is:

<CFTRY>
  <CFMODULE template="exceptionThrower.cfm" shouldThrowException="Yes">
  <CFMODULE name="coldunit.fail" message="should have thrown exception">

  <CFCATCH type="ExpectedExceptionType">
    <!--- do nothing, or, if you're really anal, you could assert that the exception message equals something you expect --->
  </CFCATCH>
</CFTRY>

This way, if the exception is not thrown, then coldunit.fail will be called, and the test will fail with the given message. If the exception is thrown, it is caught and ignored, and the test passes. Note that specifying the type of the exception caught is important here, because you do not expect any other exception type to be thrown, and if one is thrown, that would be an unanticipated error and the test should fail.


Testing presentation

Sometimes you don't just want to test your custom tags, UDFs and CFCs. Sometimes, you want to make sure, for example, that a webpage will display a given string, or that displaying the page will not throw an error. In this section, we explain how to do each of these with ColdUnit.

To test that a ColdFusion script outputs HTML containing expected strings, use the coldunit.assertContentContains tag:

<CFSET expecteds="<h1>Coin Collection</h1>,<td>total value</td><td>$0.35</td>">
<CFMODULE name="coldunit.assertContentContains" expected="#expecteds#">
  <CFINCLUDE template="displayCoinCollectionSummary.cfm">
</CFMODULE>

This will take the result of displayCoinCollectionSummary.cfm and hand it off as the actual parameter to coldunit.assertContains, which in turn will look for each element in the list, expecteds, in that content. Pretty simple. There are also tags called coldunit.assertContentNotContains and coldunit.assertNotContains that test the opposite. In their case, the relevate attribute is called unexpected, instead of expected.

Testing that a particular webpage does not throw an error under certain conditions is simple, too. I'm imagining the case where you have one ColdFusion script that retrieves data and processes it for the page, handing off the data to another ColdFusion script that actually presents the data (separating presentation from business/data access logic). In this case, you can set up the data in your test script, then include the presentation template. You should surround the include in a <CFSILENT> tag so that the page does not show up when you're running your tests. You don't have to write any asserts in your test script, because all you're testing is that the page does not throw any errors. If it does, then the test will fail, reporting the errors with stack trace to you as usual.


Testing transactional code

By now you may have realized that because ColdUnit surrounds each test file in a transaction, testing a ColdUnit script that already contains a transaction is impossible. ColdFusion does not support nested transactions. One approach to this problem is to split out the code inside the transaction into another custom tag or included template. You can then test that code normally inside ColdUnit. The transactional wrapper can be left very thin, without anything worth testing inside it.

To be a bit more thorough, there is a way to turn off the transactional wrapping that ColdUnit provides. Doing this requires that you explicitly teardown any data inserted into the database in a teardown.cfm file.

Okay, I didn't actually do this yet, but I'm about to. Look for this to be completed with ColdUnit 1.0 Release Candidate 1