Wednesday, November 10, 2010

Unit Testing Revit Plugins

Yesterday, Jeremy Tammik brought an interesting thread on augi (Unit testing with Revit API) to my attention, suggesting that I might like to add something on the RevitPythonShell (RPS).

I couldn't register as a member on augi (some mail thing I don't want to digg into), so I thought I'd add my thoughts here.

First a disclaimer: I'm not test infected. I don't floss my teeth every day either - unit testing is just one of those things you should be doing, but nothing really bad happens if you don't. Especially if you use tooth picks etc.

Unit tests (as I understand the term) cannot be dependent on an environment (in this case: Revit). So running a test as a Revit plugin with a Revit document is not a unit test. It is an integration test. I do plenty of these. Here is how:

Using the RPS, I create a "driver" script that loads the assembly to be tested (see the loadplugin module) and executes its public methods, much the same way as the IExternalCommand implementation would. Note here, that I tend to keep my IExternalCommand implementations "dumb", refactoring the logic to other parts of the assembly, so that I don't have to test the IExternalCommand itself (it should just be evident that it works, e.g. instantiate plugin object, call public method on it passing some command data, return).

The "driver" script can be registered as an RPS command and will then show up in the Revit Ribbon. You can even assign a keyboard shortcut to it using the standard Revit procedure for shortcuts (you will have to restart Revit after registering the RPS command first, as it only then gets promoted to an IExternalCommand). Run it often (after every compile)!

I generally don't register plugins that are under development with Revit, so that Revit doesn't lock the assembly on startup - this way, I can keep the source open in Visual Studio and build to my hearts content. The "driver" script will just pick up the newest build and test it.

Anything written to "Debug.WriteLine" will be output in the RPS window. This is handy for testing stuff!

Once you are at the "driver" script level, you can employ pythons unit testing libraries: unittest and also use the doc tests. This also keeps your tests separate from your production code, especially since you are using another language to write the tests!

If you really want to do unit testing, you will have to go the full monty - this is the reason for unit testing anyway:

  • Split up your code into Revit dependent functionality and modules.
  • Make sure your plugin abstracts from the Revit stuff into a logical model that you can then mock and write tests to.

  • Regard the Revit API as an I/O interface or a database connection that is not part of unit testing.

No comments:

Post a Comment