MXUnit at WebManiacs 2008 in DC

Wednesday, December 12, 2007

Come see us and many others at the WebManiacs Conference in Washington, DC, May 19-23 2008. http://www.webmaniacsconference.com/ The MXUnit team will be offering a 2 hour hands-on workshop: Implementing Test Driven Development with MXUnit.


Outline: Test Driven Development (TDD) methods have gained recognition in industry because they allow developers to quickly identify defects in their code prior to integration. Discovering defects early contributes to higher quality software. It can also greatly improve a team's productivity and cohesion.

Target Audience: ColdFusion developers who want to learn more about unit testing and automated software quality assurance.

Level: Intermediate-Advanced

Workshop Goals: By the end of this workshop, participants will be able to (1) install MXUnit Framework and Eclipse Plugin, (2) write and run unit tests, (3) build test suites that test real world applications, and(4) understand the value proposition for unit testing and be able to communicate their findings to co-workers and peers. Overview: This workshop will guide ColdFusion developers in the practice of TDD using MXUnit, an open source unit test framework and Eclipse Plugin, based on the popular JUnit unit test framework for Java.

Workshop Outline:

  1. Introduction a. Instructor introductions b. What is Test Driven Development? c. "More code?!" or Why Write Unit Tests?
  2. Setup a. System Requirements: CFMX 6 or newer, plus editor of choice. *Eclipse and CFEclipse plugin (Java 1.5 required), and Ant are ideal but not required. b. Download (or distribute on CD) MXUnit framework w/tutorial and Ant c. Install MXUnit Eclipse
  3. Writing Tests and Test Suites a. Writing simple unit tests and viewing the results in a webbrowser 1. Extending TestCase - setUp(), tearDown() 2. What is an Assertion? b. Using the MXUnit Eclipse Plugin c. Writing more complex tests 1. Testing exception handling 2. Testing database handlers 3. Security testing d. Grouping test cases into test suites e. Running test suites f. Structuring directories of tests and simple test automation g. Using the MXUnit Test Generator h. Extending and customizing MXUnit assertions
  4. Integration Practices and Automating Testing with Ant a. Overview of Integration Techniques b. Overview of Ant and basic Ant tasks c. Description of the MXUnitAntTask d. Writing an Ant task that executes single tests or directories of tests e. Using the JunitReport task to generate HTML reports of testsuites

"Imperfect tests, run frequently, are much better than perfect tests that are never written at all." -Martin Fowler

References:

  • Beck, K. Test - Driven Development by Example, Addison Wesley, 2003
  • Beck, K., Gamma, E. - JUnit Cookbook,http://junit.sourceforge.net/doc/cookbook/cookbook.htm
  • Muller, M., Padberg F., About the Return on Investment of Test-Driven Development, http://www.ipd.uka.de/mitarbeiter/muellerm/publications/edser03.pdf
  • Obermeit,Tony - Code Coverage Lessons,http://homepage.mac.com/hey.you/lessons.html
  • Fowler, Martin - Continuous Integration,http://martinfowler.com/articles/continuousIntegration.html
  • Shore, James - Continuous Integration on a Dollar a Day,http://www.jamesshore.com/Blog/Continuous-Integration-on-a-Dollar-a-Day.html .html

Bad Habit?

Tuesday, December 11, 2007

Something about unit testing in general that really struck me recently after reading http://homepage.mac.com/hey.you/lessons.html was the fact that as developers we really do unit testing all the time. This is usually in the form, code -> add cfoutput or cftrace -> Alt+Tab to browser -> Ctrl+R to refresh, and make a visual assessment of the output. If it looks good, we go back to code -> remove or comment cfoutput or cftrace -> Alt+Tab to browser -> Ctrl+R to refresh, and make a visual assessment of the output. This is the way I was doing it for most of my career. If you think about it, there is a lot of really good information contained in this activity: (1) why was a cfoutput placed in the code?, (2) what was I looking for?, and (3) what was I expecting to see in the output? Ok, what really struck me, like a ball-peen hammer upside the head, was the fact that all this intellectual property gets thrown away when we go to production. During maintenance, I then repeat the same steps over - code, trace, view output. I'm starting to think this is a bad habit. I have to recall what I was trying to do in the code in the first place and why. With a saved unit test, which takes more time up front, I have an artifact immediately at my disposal that answers these questions and let's me know when my code breaks. It also lasts for the life of the software and serves as really good documentation.

This code illustrates that I want to see that the generated output from this component is in fact XML, JUnit XML, and HTML. I would be looking for specific test result numbers in addition to format info. This is now all preserved in code so I won't forget.

<cfcomponent generatedOn="12-12-2007 4:35:38 AM EST" extends="mxunit.framework.TestCase">
 

<cffunction name="testRun">
  Tests the AntRunner and makes sure the generated content is aok
  <cfsavecontent variable="actual">
  <cfinvoke component="#this.httpAntRunner#"  method="run">
    <cfinvokeargument name="type" value="dir" />
    <cfinvokeargument name="value" value="#expandPath("../framework/fixture/fixturetests")#" />
    <cfinvokeargument name="packagename" value="mxunit.httpantrunnertests" />
    <cfinvokeargument name="outputformat" value="xml" />
  </cfinvoke>
  </cfsavecontent>
  <!--- CF parser doesn't care for xml declaration ... --->
  <cfset actual = replace(actual, '<?xml version="1.0" encoding="UTF-8"?>','','one')>
  <cfset rsDom = xmlParse(actual) />
  <cfset assertisXmlDoc(rsDom) />
  <cfset debug(rsDom.xmlroot.xmlAttributes) />
  <cfset assertEquals(rsDom.xmlroot.xmlAttributes["tests"],8,"Should only be 8 tests in this suite") />
  
  <cfsavecontent variable="actual">
  <cfinvoke component="#this.httpAntRunner#"  method="run">
    <cfinvokeargument name="type" value="dir" />
    <cfinvokeargument name="value" value="#expandPath("../framework/fixture/fixturetests")#" />
    <cfinvokeargument name="packagename" value="mxunit.httpantrunnertests" />
    <cfinvokeargument name="outputformat" value="junitxml" />
  </cfinvoke>
  </cfsavecontent>
  <cfset rsDom = xmlParse(actual) />
  <cfset assertisXmlDoc(rsDom) />
  <cfset debug(rsDom.xmlroot.xmlAttributes) />
  <cfset assertEquals(rsDom.xmlroot.xmlAttributes["tests"],8,"Should only be 8 tests in this suite") />

  
  <cfsavecontent variable="actual">
  <cfinvoke component="#this.httpAntRunner#"  method="run">
    <cfinvokeargument name="type" value="dir" />
    <cfinvokeargument name="value" value="#expandPath("../framework/fixture/fixturetests")#" />
    <cfinvokeargument name="packagename" value="mxunit.httpantrunnertests" />
    <cfinvokeargument name="outputformat" value="html" />
  </cfinvoke>
  </cfsavecontent>
  <!--- Search for this pattern:
    <title>Test Results [12/12/07 06:12:47] [127.0.0.1]</title>   
  --->
  <cfset found = refind("<title>Test Results \[[0-9]{2}/[0-9]{2}/[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}[ ]*.*</title>", actual, 0, true)>
  <cfset debug(found) />
  <cfset assertTrue(arrayLen(found.len) gt 0) />
  <cfset assertTrue(arrayLen(found.pos) gt 0) />
 
</cffunction>


<!--- Override these methods as needed. Note that the call to setUp() is Required if using a this-scoped instance--->

<cffunction name="setUp">
<!--- Assumption: Instantiate an instance of the component we want to test --->
<cfset this.httpAntRunner = createObject("component","mxunit.runner.HttpAntRunner") />
<!--- Add additional set up code here--->
</cffunction>
 

<cffunction name="tearDown"></cffunction>

</cfcomponent>