Creating Mock Objects with VisualGDB and GoogleMock

This tutorial shows how to use VisualGDB with the GoogleTest/GoogleMock framework to verify the behavior of algorithms in your code using mock classes.

Mock classes are special implementations of interfaces that record every called method and allow checking whether it was called with expected parameters. We will demonstrate it based on a basic Math class containing a Square(x) method and a function for computing the distance between 2 vectors using the Square() method and the sqrt() function. We will show how to use RefactorScript to automatically generate mock classes for C++ interfaces so that you won’t need to do it by hand.

Before you begin, install VisualGDB 5.6 or later and make sure you are using the Custom edition or higher.

  1. Start Visual Studio and locate the VisualGDB Linux Project Wizard under the File->New Project dialog:
  2. Enter the name and location for your project:
  3. On the Project Type page select “Create a new project -> Unit Test -> CMake“, then select the GoogleTest framework below:
  4. Select the computer where you would like to build and run the test and press “Next” to continue:
  5. As you are using the Advanced CMake, VisualGDB can directly access the source files on the Linux machine via SSH. However, as this tutorial is checked into Git together with the project file, we will store the sources on the Windows side and will upload them during build:
  6. Press “Finish” to create a basic Unit Test project. Build it by pressing Ctrl-Shift-B and run the tests via Test Explorer. The FailingTest should immediately fail, while the other tests should succeed:
  7. Now we will add a basic Vector struct. Add the following code to the main file, then press Ctrl+. and  select “Run a RefactorScript -> Generate a constructor”:
    struct Vector
    {
        double X, Y;
    };

    VisualGDB will automatically generate a constructor for the Square class based on the existing fields:

        Vector(double x, double y)
        		: X(x)
        		, Y(y)
        {
        }

    See our RefactorScript tutorial for more details about generating constructors.

  8. Now we will add the following code to the program:
    • A Math class providing the Square() method.
    • A GetDistance() function computing the distance between 2 vectors using the Square() method.
    • A SquareTest() unit test checking that Square() function returns expected results for a single pair of inputs

    Add the following code to your main source file:

    class Math
    {
    public:
      double Square(double arg)
      {
          return arg * arg;
      }
    };
     
    double GetDistance(Math *pMath, const Vector &a, const Vector &b)
    {
        return sqrt(pMath->Square(a.X - b.X) + pMath->Square(a.Y - b.Y));
    }
     
    TEST(DemoTestGroup, SquareTest)
    {
        Vector left(0, 0);
        Vector right(3, 4);
        Math math;
        double distance = GetDistance(&math, left, right);
        EXPECT_EQ(distance, 5);
    }
  9. Build the project and run the test, making sure it succeeds:
  10. The current version of the test checks that the GetDistance() function returned the correct result, but it doesn’t check how many times it calls the Square() method and with what arguments. We will now do exactly that – replace the real Math class with a Mock class that counts how many times Square() was called. However, before we can do that, Math needs to be refactored into the IMath interface and a Math class implementing it:
    class IMath
    {
    public:
      virtual double Square(double arg) = 0;
    };
     
    class Math : public IMath
    {
    public:
      double Square(double arg) override
      {
          return arg * arg;
      }
    };
  11. Go to the IMath class, press “Ctrl+.” and select “Run a RefactorScript -> Generate a mock class”:
  12. VisualGDB will automatically generate a mock class for the IMath interface using the GoogleMock syntax: Press “Copy and close”, insert the generated mock class in the main source file and update it to inherit the IMath interface.
  13. Finally, include the <gmock/gmock.h> file and update the SquareTest unit test as shown below:
    TEST(DemoTestGroup, SquareTest)
    {
        Vector left(0, 0);
        Vector right(3, 4);
        MockIMath math;
     
        EXPECT_CALL(math, Square(3)).WillOnce(Return(9));
        EXPECT_CALL(math, Square(4)).WillOnce(Return(16));
     
        double distance = GetDistance(&amp;math, left, right);
        EXPECT_EQ(distance, 5);
    }

    Note that EXPECT_CALL() macros. They configure the MockIMath instance to verify that it’s being called with certain arguments, and to return the specified values. You can read more about GoogleMock syntax on this page.

  14. Run the test. It will now fail with a detailed message explaining that Square() was called the arg=-3 that doesn’t match neither 3 nor 4:
  15. Update the EXPECT_CALL() macros to expect the values of -3 and -4 respectively. The test will now pass:
  16. You can tweak the code produced by RefactorScripts by directly editing the script. VisualGDB will immediately update the output. E.g. change first 2 lines of the DoGenerateMockClass() generator to automatically strip “I” from the class name and to inherit the interface:
    set strippedName = class.ShortName.StripPrefix("I");
    &gt;class Mock$strippedName : public $class.QualifiedName

  17. The GenerateMockClass script can automatically append the const qualifier for the const methods, however it won’t automatically add the override keyword. To change this, locate the logic for outputting “(const)” near the end of the generator and replace it with the following:
    if (method.IsConst || method.IsVirtual)
    {
    	&gt;&gt;, (
    	if (method.IsConst)
    		&gt;&gt;const
    	if (method.IsConst &amp;&amp; method.IsVirtual)
    		&gt;&gt;, 
    	if (method.IsVirtual)
    		&gt;&gt;override
    	&gt;&gt;)
    }

    Now all virtual methods will be marked as “(override)”:

  18. Press “Copy and close” to copy the updated mock class into the Clipboard so that you can replace the original implementation: