Unit Testing for Native Applications Using WinUnit

Introduction to WinUnit

Software development has changed a lot since the 70s. No longer is it simply called software development, it's now called software engineering where the demand is not only for the product but for a product with quality. With .NET framework came a slew of tools to ensure code quality. Nothing got more limelight than NUnit, the tool for unit testing. Despite being in the industry for around 27 years, there hasn't been an equivalent good offering for C++ programming to handle unit testing.

What is Unit Testing

Per Wikipedia, "unit testing" is defined as the method to ensure even the smallest testable portion of your software is working as expected. This small part can be as small as a function which determines if a number if even or odd; or it can be more complex. Unit test authoring involves testing functionality in isolation. That ensures that even the smallest part is doing the job it is supposed to do. Imagine what would happen is NASA space shuttles did not use unit testing in their algorithms to determine if the launch speed was within the acceptable range or not.

Unit Testing Features Available for Programmers

There are a range of unit testing frameworks available for programmers http://en.wikipedia.org/wiki/List_of_unit_testing_frameworks . Depending on the programming platform/language, developers/testers have a bunch of choice in unit testing tools/frameworks. However not all frameworks/tools are available in the language of choice. Case in point, NUnit (the ultra-popular does not have a simple equivalent in the native world.

Introducing WinUnit

WinUnit was authored by Microsoft engineer Maria Blees and is available for download as a free tool at http://winunit.codeplex.com/ . WinUnit offers a NUnit type of unit testing experience. The WinUnit download contains an executable which run the test (available in x86 and x64), header files, logger extensibility library and macros for use inside of Microsoft Visual Studio.

How WinUnit Works

WinUnit works only on code which is refactored into a library. If the only entry point to your algorithm is to instantiate the UI of the application and then click a button, you have some work to do. You will have to move out the logic of your application to be tested into a DLL/lib file.

To create WinUnit test functions, you create a ".cpp" file. To start off, you use the macro BEGIN_TEST and provide it a test name. To check a criterion, WinUnit provides macros like WIN_ASSERT_EQUAL, WIN_ASSERT_TRUE, WIN_ASSERT_NOT_EQUAL, WIN_ASSERT_STRING_EQUAL, etc to check conditions. You then end your test with the macro END_TEST.

Once you have your test read, you compile your test file using the following command-line:

  cl.exe /EHsc /LD <WinunitTestFile.cpp>

Once your test file is compiled into a DLL, you then use the WinUnit.exe (from the WinUnit download) to run the test.

   <PathtoWinUnit>\WinUnit.exe WinUnitTestFile.dll
   WinUnit will run the tests for you and report any failures as under
   Processing [WInUnitTestFile.dll]...
   (WinUnitTestFile)
   WinUnitTestFile.cpp(6): error : WIN_ASSERT_TRUE failed: "1 == 0".
   FAILED: DemoWinUnit.
    [WinUnitTestFile.dll] FAILED.  Tests run: 1; Failures: 1.
 

How to write efficient test code using WinUnit without polluting your source code.

Let us have a hands-on example of how to write efficient unit testing code using WinUnit. For our case, let us assume we have an extremely complex function which determines if a provided number is even or odd. It returns 0 if the input was even and 1 if the input was off.

Here is how the sample code looks.

  // Listing 1 - EvenOrOdd.h
  class EvenOddDeterminator
  {
  
  	public:	
  		EvenOddDeterminator();	
  		~EvenOddDeterminator();	
  		int IsItEven(int input);	
  };
  
  // Listing 2 - EvenOrOdd.cpp
   #include "EvenOrOdd.h"
  EvenOddDeterminator::EvenOddDeterminator()
  {
  }
  EvenOddDeterminator::~EvenOddDeterminator()
  {
  }
  int EvenOddDeterminator::IsItEven(int input)
  {
  
  		if ((input % 2) != 0)
  			return 0;
  		else
  			return -1;
  
  }

As you can see above, we have a bug where we calculate where the input value is even or not. To see how to write a WinUnit test, let's take a look at the listing 3 below.

  // Listing 3 - DemoWinUnit.cpp
  #include "..\\WinUnit-1.2.0909.1\\Include\\winunit.h" // TODO Set the correct path to WinUnit.h on your machine
  #include "EvenOrOdd.cpp"
  BEGIN_TEST(DemoWinUnit)
  {
     EvenOddDeterminator c ;
     WIN_ASSERT_FALSE (c.IsItEven(3) == 0, _T("You should not see this error message."));
  }
  END_TEST
  
  
  BEGIN_TEST(DemoWinUnit2)
  {
     EvenOddDeterminator c ;
     WIN_ASSERT_TRUE(c.IsItEven(2) == 0, _T("You should not see this error message."));
  }
  END_TEST

If we look closer at the DemoWinUnit.cpp code, we find that we have two tests - DemoWinUnit and DemoWinUnit2. The first test, DemoWinUnit, checks that when we pass 3, an odd number, to the function, we should get not get zero, which we ascertain by using the macro WIN_ASSERT_FALSE which means that this condition should not pass.



Unit Testing for Native Applications Using WinUnit

For the second test, we check that the IsItEven function returns 0 when we pass in the value 2. To compile our test, we execute the following command:

  cl /EHsc /LD DemoWinUnit.cpp

This compiles our test as a DLL.

Next, we execute our test binary using WinUnit.exe. For this we need to ensure that we use the version of WinUnit with the right bitness (x86 version of winunit.exe for 32 bit DLLs and x64 version of WinUnit.exe for 64 bit DLLs.

  ..\WinUnit-1.2.0909.1\Bin\x64\WinUnit.exe DemoWinUnit.dll

When we execute the above command line, the tests execute and we see the following output.

  Processing [DemoWinUnit.dll]...
  (DemoWinUnit)
  DemoWinUnit.cpp(6): error : WIN_ASSERT_FALSE failed: "c.IsItEven(3) == 0". You should not see this error message.
  FAILED: DemoWinUnit.
  (DemoWinUnit2)
  DemoWinUnit.cpp(14): error : WIN_ASSERT_TRUE failed: "c.IsItEven(2) == 0". You should not see this error message.
  FAILED: DemoWinUnit2.
  [DemoWinUnit.dll] FAILED.  Tests run: 2; Failures: 2.

There were errors.
Tests run: 2; Failed: 2.

We see that our unit tests have failed. While this may be disheartening to know that your unit tests have failed, we have saved a lot of servicing and testing time by catching this early on.

At this point, we examine the highlighted code below and can easily find our mistake.

  // Snippet in EvenOrOdd.cpp
  int EvenOddDeterminator::IsItEven(int input)
  {
  
  		if ((input % 2) != 0)
  			return 0;
  		else
  			return -1;
  
  }

Instead of checking that the remainder should be equal to zero, we had coded that the reminder should not be zero. Now, that is an easy fix.

  {
  
  		if ((input % 2) == 0)
  			return 0;
  		else
  			return -1;
  
  }

The fix... simply changing "!=" to "==".

Now if we recompile our tests and rerun WinUnit, we will see that our tests will pass.

  Processing [DemoWinUnit.dll]...
  (DemoWinUnit)
  (DemoWinUnit2)
  [DemoWinUnit.dll] SUCCEEDED.  Tests run: 2; Failures: 0.


All tests passed.
Tests run: 2; Failed: 0.

While the above might be a very simple code, you can extrapolate the steps we took to fit into your complex C++ application.

Conclusion

In this article, we saw how we can Unit-test our C++ programming



About the Author

Vipul Vipul Patel

Vipul Patel is a Software Engineer currently working at Microsoft Corporation, working in the Office Communications Group and has worked in the .NET team earlier in the Base Class libraries and the Debugging and Profiling team. He can be reached at vipul_d_patel@hotmail.com

Downloads

Comments

  • There are no comments yet. Be the first to comment!

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • On-demand Event Event Date: September 10, 2014 Modern mobile applications connect systems-of-engagement (mobile apps) with systems-of-record (traditional IT) to deliver new and innovative business value. But the lifecycle for development of mobile apps is also new and different. Emerging trends in mobile development call for faster delivery of incremental features, coupled with feedback from the users of the app "in the wild." This loop of continuous delivery and continuous feedback is how the best mobile …

  • On-demand Event Event Date: July 22, 2014 In this WhatWorks analysis, John Pescatore examines a use case where end users had local administrative rights on their PCs and it had gotten out of hand for this Fortune 500 Energy and Utilities company. The compelling event that prompted the company to reexamine this situation was the migration to Windows 7. In Windows XP, a custom tool that allowed users one of three levels of administrative rights to their workstations would need to be replaced during the Windows …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds