Unit testing native C++ code using Visual Studio

Before I begin with this post, let me state that Unit testing native code with Test Explorer explains this topic very well. So why am I writing a post on this? Well I did encounter a couple of issues and felt it worth documenting those along with the “steps” I took to when using the Microsoft unit testing solution for C++ in Visual Studio.

Let’s jump straight in – my intention is to create a simple class which has some setter and getter methods (nothing special) and test the implementations, obviously this is just a simple example but it will cover some of the fundamentals (I hope).

Here’s the steps to get a couple of Visual Studio projects up and running, one being the code we want to test, the second being for the unit tests.

  • Let’s start by creating a DLL for our library/code.
  • Open Visual Studio and create a New Project
  • Select Visual C++ Win32 Project and give it a name (mine’s named MotorController), then press OK
  • When the Win32 Application Wizard appears, press next then select DLL for Application Type, uncheck Security Development Lifecycle and check Export Symbols. I’m not going to use MFC or ATL for this so leave them unchecked, then press Finish
  • Now let’s create the unit test project
  • Select Add | New Project from the solution context menu
  • Select the Visual C++ | Test section and click on Native Unit Test Project
  • Give the unit test project a name (mine’s MotorControllerTests), then press OK
  • Before we can test anything in the MotorController project we need to reference the project
  • Right mouse click on your unit test project and select Properties
  • Select Common Properties | Framework and References
  • Press the Add New Reference button and check the project with code to be tested (i.e. my MotorController project), press OK and OK again on Properties dialog

At this point you should have two projects, one for your code and one for your unit tests. Both are DLL’s and the unit test project includes the code to run the tests via the Test Explorer.

So before we write any code and to ensure all is working, feel free to run the tests…

Select the menu item – Test | Run | Run All Tests. If all goes well, within Test Explorer, you’ll see a single test class named UnitTest1 (unless you renamed this) and a single method TestMethod1 (unless you changed this).

Now let’s go ahead and write some tests. I’m going to assume you’ve used the same names as I have for the objects etc. but feel free to change the code to suit your object names etc.

  • Rename the TEST_CLASS from UnitTest1 to MotorControllerTest and change the file name of the unit test to match (i.e. MotorControllerTest.cpp)
  • We’re going to need access to the header file for the MotorController class so add an include to the MotorControllerTest.cpp file to include the “MotorController.h” header, I’m going to simply use the following for now (i.e. I’m not going to set up the include folders in VC++)
    #include "../MotorController/MotorController.h"
    
  • We’re going to implement a couple of simple setter and getter methods to demonstrate the concepts of unit testing with Visual Studio. So to begin with let’s rename the current TEST_METHOD to getSpeed, then add another TEST_METHOD named getDirection, so your code should like like this
    TEST_CLASS(MotorControllerTest)
    {
    public:
       TEST_METHOD(getSpeed)
       {
       }
    
       TEST_METHOD(getDirection)
       {
       }    
    };
    
  • Now if we run these tests we’ll see our newly named class and two test methods are green, as we’ve not implement the code this might be a little off putting so you can always insert the Assert::Fail line into your unit test method until it’s implemented, for example
    TEST_METHOD(getSpeed)
    {
       Assert::Fail();
    }
    

    If you now run your tests (assuming you placed the Assert::Fail into your methods) they will both fail, which is as expected until such time as we implement the code to make them pass.

  • To save going through each step in creating the code, I’ll now supply the unit test code for the final tests
    TEST_CLASS(MotorControllerTest)
    {
    public:
    		
       TEST_METHOD(getSpeed)
       {
          CMotorController motor;
          motor.setSpeed(123);
    
          Assert::AreEqual(123, motor.getSpeed());
       }
    
       TEST_METHOD(getDirection)
       {
          CMotorController motor;
          motor.setDirection(Forward);
    
          Assert::AreEqual(Forward, motor.getDirection());
       }    
    };
    
  • Next let’s implement some code in the MotorController.h and MotorController.cpp
    // MotorController.h
    
    enum Direction
    {
        Forward,
        Reverse
    };
    
    // This class is exported from the MotorController.dll
    class MOTORCONTROLLER_API CMotorController {
    private:
        int speed;
        Direction direction;
    public:
    	CMotorController(void);
    
        void setSpeed(int speed);
        int getSpeed();
    
        void setDirection(Direction direction);
        Direction getDirection();
    };
    
    

    and

    // MotorController.cpp
    
    CMotorController::CMotorController()
    {
    }
    
    void CMotorController::setSpeed(int speed)
    {
        this->speed = speed;
    }
    
    int CMotorController::getSpeed()
    {
        return speed;
    }
    
    void CMotorController::setDirection(Direction direction)
    {
        this->direction = direction;
    }
    
    Direction CMotorController::getDirection()
    {
        return direction;
    }
    
  • If you run these tests you’ll find a compiler error, something along the lines of

    Error 1 error C2338: Test writer must define specialization of ToString for your class class std::basic_string,class std::allocator > __cdecl Microsoft::VisualStudio::CppUnitTestFramework::ToString(const enum Direction &). c:\program files (x86)\microsoft visual studio 11.0\vc\unittest\include\cppunittestassert.h 66 1 MotorControllerTests

    The problem here is that we’ve introduced a type which we have no ToString method for within the CppUnitTestAssert.h header, so we need to add one. Simply insert the following code before your TEST_CLASS

    namespace Microsoft{ namespace VisualStudio {namespace CppUnitTestFramework 
    {
        template<> static std::wstring ToString<Direction>(const Direction& direction) 
        { 
           return direction == Forward ? L"F" : L"R"; 
        };
    }}}
    

    The concatenation of the namespace on a single line is obviously not neccesary, I just copied the way the CppUnitTestAssert.h file had their namespace and also it ensures I can easily show you the main code for this. What does matter though is that we’ve implemented a new ToString which understands the Direction type/enum.

  • Finally, run the tests and see what the outcome is – both tests should pass, feel free to break the getter code to prove the SUT is really being tested

That should be enough to get your unit testing in VC++ up and running.