MSDN Magazine - February 2008 - (Page 75) WinUnit will accept any number of DLLs or directories, process them, report the total results, and return with an exit code. By the way, if you haven’t guessed, WinUnit only works on Windows®. I concede that the world was not entirely devoid of options for C++ unit testing before I wrote my own tool. The problem I found with existing offerings was that they generally placed a higher priority on portability than on being easy to understand and use out of the box. CppUnitLite is my favorite among the portable set, but because it’s designed to be very customizable, there was more overhead than I would have liked in getting it set up. The line between test-runner and tests is blurry due to the fact that it’s all done within the same binary, and the code that actually invokes the testrunning functionality needs to be written and put somewhere. In addition, the tricky macro implementation was hard to explain to my coworkers and thus made adoption difficult. However, if you require portability to other operating systems, CppUnitLite is definitely worth taking a look at. Other options that have been available for C++ unit testing for quite some time are the same options available for testing .NET code, most notably NUnit and the Visual Studio® Team Test unit testing framework. Since both will execute any properly attributed .NET assembly, you can use these tools with tests written in managed C++ (now C++/CLI). The main inconvenience I find with this approach is that the tools are highly geared toward .NET development and not toward native code. This should not be a surprise. But it is hard to use most of the provided assert methods with native C++ for they expect .NET-style objects, and as a developer you will need to have at least a passing familiarity with C++/CLI (or Managed Extensions for C++) to write the tests. You’ll find more details about limitations with this approach in the footnote on the Visual Studio 2005 Product Feature Comparisons page (msdn2.microsoft.com/vstudio/aa700921.aspx). As mentioned previously, I find the “ease of convincing my coworkers to use it” factor important, and using the managed tools provides an extra hurdle in this area. Start Testing Today Before getting into WinUnit usage, I want to discuss my philosophy about unit testing in general and unit testing native code in particular. If you’re like me, you may have noticed that the clean, elegant, and modular classes shown in unit testing examples seem to have nothing in common with the blob of legacy code you typically have to work with. If you think that you must rework your entire code base before you can unit test, I can tell you those tests are going to be a long time coming. The good news is that a testable unit doesn’t have to be a clean, elegant class. It’s simply the smallest unit of code that can be tested in isolation. If that’s your whole application, well, there’s no better time to start than now! Seriously, if your application consists of exactly one executable and its only interface is through a GUI, you may have to do a bit of refactoring to make the non-GUI part automatable. However, it can certainly be done incrementally. I recommend reading the whitepaper “The Humble Dialog Box,” by Michael Feathers, for a sensible approach to making a GUI application more testable (objectmentor.com/resources/articles/TheHumbleDialogBox.pdf). Your ultimate goal should be to have automated developer tests running as part of your build process, making it as easy as possible for developers to add tests as they fix bugs or write new code. Once the infrastructure is set up, you can work on refactoring existing code to allow better test coverage in problem areas. The book Working Effectively with Legacy Code, also by Michael Feathers (Prentice Hall, 2004), is an excellent resource for guiding you through this process (and many of the examples are in C++). If your application is already split up into static libraries with clearly defined interfaces, you can link these directly into test DLLs and you’ll be able to get at the individual classes to test them. Consider having one test DLL per library to maintain clear interface boundaries. If your application conThe good news is that a tains separate DLLs, or is testable unit doesn’t have itself a library provided in to be a clean, elegant class. DLL form, there are two ways you can access the It’s simply the smallest unit exports to exercise them of code that can be tested in in tests. If you use import isolation. libraries, you can link those into test DLLs and have transparent access to the production functions for testing. If you do not use import libraries, use GetProcAddress in conjunction with LoadLibrary and FreeLibrary to get the DLL exports to call directly in your tests. Note that if your DLL is a COM DLL, it doesn’t need to be registered to be tested. Windows XP and later offer a mechanism called “Registration-Free COM,” which you could use to get around the usual registration requirement for COM DLLs. You’ll find details at msdn2.microsoft.com/ms973913. As a bonus, once you’ve gotten it working for tests, you can use the feature in your production code. If your application is a monolithic executable, you probably want to split it up so you have at least one library. But you might also want to test the executable in its entirety to check for proper exit codes, for example. In my unit tests for WinUnit itself, I included some tests that exercised the entire executable. You can find these within the TestWinUnit project—in the MainTest.cpp file—in the accompanying code download. At risk of coming across as the style police, there’s one point about physically organizing production code that I would be remiss if I did not mention. In my years at Microsoft working on large C++ projects, I’ve found that one thing that made modularity (and testability) exceedingly difficult was having multiple classes jumbled together across arbitrary .cpp and .h files. It’s up to you how strictly you adhere to this practice in your production code, but I find associating one and only one class with a similarly named pair of .cpp and .h files to be helpful. As a bonus, it simplifies keeping your tests organized, with one matching test file for each class. You can see some examples in my projects WinUnitLib and TestWinUnit. There is one other book I recommend reading if the whole concept of unit testing has just passed you by, or if you’re a developer in need of a refresher on how to think like a tester: Pragmatic Unit Testing with C# in NUnit, by Andrew Hunt and David Thomas, february2008 75 http://msdn2.microsoft.com/ms973913 http://msdn2.microsoft.com/vstudio/aa700921.aspx http://msdn2.microsoft.com/vstudio/aa700921.aspx http://objectmentor.com/resources/articles/TheHumbleDialogBox.pdf http://objectmentor.com/resources/articles/TheHumbleDialogBox.pdf
For optimal viewing of this digital publication, please enable JavaScript and then refresh the page. If you would like to try to load the digital publication without using Flash Player detection, please click here.