MXUnit 1.0.3 Now Available

Thursday, September 25, 2008

A new release of MXUnit is now available. Get it while it's hot. 1.0.3 comprises a "Bug Fix" and a feature. The bug in question was thoroughly described by Bob Silverberg here. In a nutshell, the eclipse plugin was bombing when an error was thrown containing a non-string error "Type". This required a simple fix to RemoteFacade.cfc. Props to Bob for finding the bug and working with me to pinpoint the source of the problem! Bob sent me a self-contained set of files that clearly demonstrated the issue. It took me literally 30 seconds to see what he was talking about. A lot longer to identify the source of the problem, mind you, but because Bob took the time to make it easy to duplicate the problem, this bug was fixed really fast. The feature came from a suggestion by Randy Merill to enable DirectoryTestSuite to work with relative paths. Previously, it required an absolute path as the Directory argument. Now, it'll take a relative path and try to resolve it using expandPath(). The full discussion is here. Props to Randy for the suggestion and for contributing the code to make it happen. Happy testing. --marc

Designing for testability: today's real-world example

Monday, September 8, 2008

I'm at work now, staring at Eclipse and a method of approximately 150 lines. It's a method for performing sort order updates. Someone else, long gone, wrote it. It's not ugly code by any means... just complex. And there's a bug. A big part of me wants to work through this bug by testing it in the interface. Change the sort orders in the form, hit Submit, add dumps and aborts, comment out the actual database update in the code, and struggle through till I fix the sucker. But damn, I just know that's gonna be painful and slow. And if I'm going to spend the time fixing it, I might as well capture my work. So a-unit-testin' I will go. This is the first time I've seen this code, and since I gotta fix a bug in it, I'm wondering.... how easy will it be to write a unit test for this code. As with most of our stuff, it's database-heavy. And I don't want to let it perform actual database updates and then test the database after-the-fact to confirm that the updates I wanted to happen actually happen. That right there is one way to describe hard-to-test code: you have to perform updates, then refetch the data to see if the results you expected are true. This code I'm looking at has several loops, many nested. I was hoping not to see a bunch of CFQueries inside the loops. This would've been a rat's nest, virtually impossible to test well. But instead, there's only a single CFQuery chunk, at the bottom of the method. The original programmer did a really smart thing: He built up a big ol' array of the updates he wanted to perform, and then in the end looped over the array and did the database work. This is great for 2 reasons:
  1. It's a lot easier to test arrays for the data they contain than it is to test the database after the fact
  2. Since there's only one chunk of code that performs a database Update, I can easily "turn the update off", thereby not updating the database at all.
#2 probably needs some more explanation, so let me show. Here's what the code basically looked like to start. I've omitted all the actual logic since it's not important here. The important part is that there's only a single block for the queries, and that block is at the end of the original method:
<cffunction name="moveSection" returntype="array" hint="resorts stuff" access="public">
  <cfargument name="blah">

  <cfset var sections = ArrayNew(1)>
  <cfset var ii = 1>
  <cfset var jj = 1>
  <cfset var kk = 1>
  <cfset var keyList = "">
  <cfset var updateTemplateSections = ""> 
  <!--- assload of logic to build up an array --->
  .....

  <!--- process array and extract sectionIDs in order --->
  <cfloop from="1" to="#arrayLen(sections)#" index="ii">
      <cfset keyList = listAppend(keyList,sections[ii].sectionID)>
      <cfloop from="1" to="#arrayLen(sections[ii].children)#" index="jj">
          <cfset keyList = listAppend(keyList,sections[ii].children[jj].sectionID) />
      </cfloop>
  </cfloop>

  <!--- finally, do the update ---> 
  <cfloop from="1" to="#listLen(keyList)#" index="kk">
      <cfquery name="updateTemplateSections" datasource="#getDsn()#">
          UPDATE    templateSections
          SET        sortOrder = #kk#
          WHERE    templateSectionId = #listGetAt(keyList,kk)#
      </cfquery>
  </cfloop>

  <cfreturn sections>

</cffunction>
As I said, in my test, I want to do two things: Test the array for its data, and ensure the database update doesn't occur. This method, as originally written, almost gets me there. To the original programmer (my friend Mike), nice job! The obvious problem here, with respect to testing, is that the update will still happen. So what to do? Since the original code has a single block for the DB update, it's easy for me to pull out that chunk of code into a separate method, and then inside my test, override that method with one that simply doesn't perform any updates. I call this "Extract and Override". Here's my new code. It now contains 2 methods. I've simply extracted out that last chunk of DB updates into a separate method, and then I call that in the original function:
<cffunction name="moveSection" returntype="array" hint="resorts stuff" access="public">
  <cfargument name="blah">

  <cfset var sections = ArrayNew(1)>
  <cfset var ii = 1>
  <cfset var jj = 1>

  <cfset var keyList = "">
 
  <!--- assload of logic to build up an array --->
  .....

  <!--- process array and extract sectionIDs in order --->
  .....

  <!--- call my new method, which I've extracted --->
  <cfset performMoveSectionUpdate(keyList)>

  <cfreturn sections>

</cffunction>

<cffunction name="performMoveSectionUpdate" access="private">
  <cfargument name="keyList">
  <cfset var kk = 1>
  <cfset var updateTemplateSections = "">
  <!--- finally, do the update ---> 
  <cfloop from="1" to="#listLen(keyList)#" index="kk">
      <cfquery name="updateTemplateSections" datasource="#getDsn()#">
          UPDATE    templateSections
          SET        sortOrder = #kk#
          WHERE    templateSectionId = #listGetAt(keyList,kk)#
      </cfquery>
  </cfloop>
</cffunction>
Finally, the unit test. Since I've extracted out the actual code that performs the update, it's trivial to override that with MXUnit in the test. All I do is create a new, private function inside the test itself that does nothing. Then, I use injectMethod to replace the existing function with my new for-testing-only function:
<cffunction name="moveSectionDownResortsCorrectly">
      <cfset injectMethod(gw, this, "performMoveSectionUpdate", "performMoveSectionUpdate")>
      <cfset sections = gw.moveSection(arg1,arg2,arg3)>
      <!--- perform assertions as needed --->
   
  </cffunction>

  <cffunction name="performMoveSectionUpdate" access="private">
      <cfreturn "">
  </cffunction>
Bottom line:
  1. in my original object, extract out the behavior that I don't want to occur, during my test, into a separate method
  2. in the test, override that method in my original object with a new, innocuous method
I'm now free to test the arrays for their data without fear of corrupting the database since I'm no longer performing any updates in the method I want to test. This leads to a more predictable, less brittle, faster test. -- marc

RIA as Motivator for Server-Side Test-Driven Development


Carl Jung coined the term "synchronicity" to describe events that at first appear coincidental but which are potentially meaningfully connected.... a "meaningful coincidence". I've been thinking about this for some time now: that the advent of Rich Internet Applications (RIAs) could potentially be a tremendous boon for encouraging boring old server-side unit testing. Now, you might be thinking: "the two are completely unrelated". You can't get much more disparate. RIAs are sexy, they're hip, they're sleek and fun and whizbang. Unit testing, on the other hand, has got to be the least sexy coding you can do. It's the toothless, saggy-butt, scraggy-haired 4th cousin of cool technologies, at least to many people. Cranking out animated heatmap hoogies in flex is toot sweet. Writing "assertEquals(4,query.recordcount)" ain't.

Back when I started writing web apps in CF, debugging was pretty simple because you had easy access to the information you needed in order to debug. You load a page. If you have problems, you maybe throw in a cfdump/cfabort. Or you submit a form and want to see that the DB update query code looks OK, so you comment out the redirect in your index.cfm file, and then look at the debug output at the bottom of the page to check that your database updates are as expected. Snip snap done. But when you're building richer interfaces, this work is often either done asynchronously using javascript (ajax), in which case you can't rely on your old tricks; or, even more difficult, it's done in a movie client (flex/flash, silverlight) and you're even further removed from the information you need in order to do your debugging.

The further removed you are from development-time access to debug information, the more time you spend debugging development-time problems.

Let that sink in. There is no cfdump/cfabort simplicity when your front end is a flash movie making remote calls to your backend CFCs. With richer interfaces, the time you spend debugging your backend code increases considerably. Not so sexy anymore, is it? But here's the thing: What if, rather than try to debug your code at runtime in the interface, you instead remove the interface from the equation. You stay in CF for your debugging. You get your CFDumps back. In short, you get closer to your data again... you get closer to the information you need to do your debugging. You're no longer so far removed.

This is where our ugly cousin, unit testing, comes in for the win. By testing your backend CF code with more CF code, you stay on the server and you catch your problems earlier. You codify the expectations of the code. You think more about the edge cases. You root out bugs earlier in the process and thereby make writing your RIA joyful again. You can focus your front-end efforts on the pizazz because you're not worrying so much that your queries are working as expected -- you've already worked out the server-side kinks in your tests. The common arguments against unit testing -- that it takes too much time, that you write too much code, etc -- start to fall away as it becomes apparent that the time savings you accrue start to overtake the lost time spent trying to debug server-side code from an RIA.

This is what I've been thinking about: that RIAs make it harder to debug server-side code. Therefore, they encourage more unit testing of server-side code because the value proposition of unit testing is clearer when put in contrast to the drudgery of runtime debugging the backend of an RIA.

Which leads to my moment of synchronicity today. I had planned on writing this blog post on how RIA development damn near requires backend TDD. Then, I came to work this morning, and in my feedreader was this: Indy Nagpal gave a presentation on server-side TDD at a recent RIA conference. Not at CFUnited or CFObjective or the local CFUG. At an RIA conference. I thought that was pretty cool, and it suggests to me that other (way smarter) people are thinking the same thing: that if you're writing rich internet apps, then server-side unit testing is getting harder to ignore.

--marc

MXUnit Eclipse Plugin Test History Feature

Thursday, September 4, 2008

I'm happy to announce the latest feature to the MXUnit Eclipse plugin: Test History. Every time you run a test (or directory of tests), it adds that test to your history. When you want to revisit a test, you can open up the history menu, find your test, and reload it. Here's a screen shot to demonstrate: There are a few differences between the JUnit and MXUnit implementations of this functionality, born out of my own frustration with the JUnit plugin. I decided that if I were going to add test history, then I was going to make it work how I always wished the JUnit plugin would behave.
  1. No duplicate entries. "com.my.SomeTest" will only ever appear once in the list, and will only reflect the last run. If you run that test 20 times, you won't get 20 entries in the list
  2. When running a directory of tests, you see the full path to the directory in the list
  3. It's easy to distinguish between single TestCase runs and Directory runs. Simply, directory runs start with a slash.
  4. A more reasonable start value for remembered test runs. JUnit defaults to 10 items in the history. Dunno why. MXUnit defaults to 30. Considering that duplicates never appear, this seems like a reasonable default. In addition, changing the default is simple and obvious.
There's one thing to note, and I'm still not sure how I feel about this because I haven't worked with it enough to form an opinion yet: Tests retain their relative position in the test history no matter how many times they are run. Let me explain: You run Test1.cfc at 7:12:00 AM. This test gets position 1. You run Test2.cfc at 7:12:15 AM. This test gets position 1. Test1 is bumped to position 2 (i.e. it is lower in the list of tests in the menu). You run /mytests/ at 7:13:30 AM. This test gets position 1. Test2 is bumped to position 2, and Test1 is bumped to position 3. So far, this is how you'd expect it to work. Here's where it changes: You then select Test1.cfc from the test history and re-run that test. Now, since this last run makes it "newer", in a sense, you might expect it to show up at the top of the list. However, I am currently opting to keep that test in its original position. This might seem weird, but I'll ask you to reserve judgment until you've had a chance to work with it a bit. As always, feedback is welcome. Happy testing!