c++unit-testingvisual-c++googlemockmicrosoft-cpp-unit-test

How to use Google Mock with CppUnitTestFramework


TL;DR: You can use GMock to add mocking capability to your Microsoft native c++ unit tests. See my answer below for details.


I want to start adding mocks to my existing set of native unit tests. The tests are written using Microsoft's CppUnitTestFramework framework, which doesn't have support for mocking. I don't really want to convert the entire test suite to another framework just to add a few mocks.

Google's GMock framework seems to provide everything I need and the documentation suggests it can be used with frameworks other than gtest. So using advice from blog posts like this one, I created a couple of unit tests.

    TEST_MODULE_INITIALIZE(ModuleInitialize)
    {
        // Enable google mock
        GTEST_FLAG(throw_on_failure) = true;
        int argc = 0;
        TCHAR **argv = nullptr;
        InitGoogleMock(&argc, argv);
    }

    TEST_CLASS(GMockTests)
    {
    public:
        MockTestClass _mockObj;

        TEST_METHOD(Method1_ParamIsOne_Method2CalledWithOne)
        {
            EXPECT_CALL(_mockObj, Method2(1)).Times(1);
            _mockObj.Method1(1);
        }

        TEST_METHOD(Method1_ParamIsZero_IntentionallyFail)
        {
            // Expectation will fail
            EXPECT_CALL(_mockObj, Method2(1)).Times(1);
            _mockObj.Method1(0);
        }

    };

The results are less than satisfactory. The expectations do work (first method passes), but if any expectation fails the entire run is aborted with only the following unhelpful message in the test output:

[3/27/2019 11:39:17 AM Error] The active test run was aborted. Reason: 
[3/27/2019 11:39:17 AM Informational] ========== Run test finished: 0 run (0:00:22.3042194) ==========

The Visual Studio Test Explorer window doesn't indicate what the problem is either. It just shows that one test succeeded and the other one didn't run:

Test Explorer screen snip

So what I'm looking for from this integration is:

  1. A GMock test failure doesn't abort the entire run.
  2. Tests that fail GMock expectations are shown as failed.
  3. All GMock messages should be shown in the test output.

Solution

  • I was able to get GMock working correctly with the CppUnitTestFramework by creating a custom TestEventListener. I then created a simple set of interface functions to make it easier to work with.

    I used the gmock 1.7.0 NuGet package to install the GMock framework into my project, and added the two files shown below.

    GMockUtils.h

    #pragma once
    
    #include <CppUnitTest.h>
    #include <gmock/gmock.h>
    
    namespace testing { 
        namespace GMockUtils {
    
            // Call once per test class or module to set up everything needed by GoogleMock.
            void InitGoogleMock();
    
            // Call once per test method to check for GoogleMock messages.
            void CheckGoogleMock();
        }
    }
    

    GMockUtils.cpp

    #include "stdafx.h"
    #include "GMockUtils.h"
    
    using namespace Microsoft::VisualStudio::CppUnitTestFramework;
    
    namespace testing {
        namespace GMockUtils {
            namespace {
    
                // Test event listener for use with CppUnitTestFramework
                class CppUnitTestReporter : public EmptyTestEventListener
                {
                public:
                    CppUnitTestReporter() : _failed(false) 
                    {
                    }
    
                    // Helper for converting std::string to std::wstring
                    std::wstring to_wstring(const std::string& str) const
                    {
                        std::wstring output;
                        std::copy(str.begin(), str.end(), std::back_inserter(output));
                        return output;
                    }
    
                    // Called after a failed assertion or a SUCCEED() invocation.
                    void OnTestPartResult(const ::testing::TestPartResult& result) override
                    {
                        // Log this result to the CppUnitTestFramework output
                        Logger::WriteMessage(result.summary());
    
                        // Note: You cannot do an Assert directly from a listener, so we
                        // just store the messages until CheckGoogleMock() is called.
                        if (result.failed())
                        {
                            _failed = true;
    
                            // Append this result to the running summary
                            _failedSummary += result.message();
                            _failedSummary += "\n";
                        }
                    }
    
                    // Clear any previous failures
                    void ResetFailures()
                    {
                        _failed = false;
                        _failedSummary.clear();
                    }
    
                    // Assert if any failures have been detected. Also resets failures.
                    void CheckFailures()
                    {
                        auto failed = _failed;
                        auto failedSummary = _failedSummary;
                        ResetFailures();
                        Assert::IsFalse(failed, to_wstring(failedSummary).c_str());
                    }
    
                protected:
                    bool _failed;
                    std::string _failedSummary;
    
                } *_listener;
            }
    
            // Initialize the Google Mock framework for use with CppUnitTestFramework
            void InitGoogleMock()
            {
                // Avoids calling the function unnecessarily
                if (_listener != nullptr)
                    return;
    
                // Dummy command line parameters (could pass exe path here)
                int argc = 0;
                char** argv = nullptr;
    
                // Initialize the framework
                ::testing::InitGoogleMock(&argc, argv);
    
                // We don't want exceptions thrown, regardless what the doc says
                GTEST_FLAG(throw_on_failure) = false;
    
                // Remove default listener
                auto &listeners = UnitTest::GetInstance()->listeners();
                delete listeners.Release(listeners.default_result_printer());
    
                // Create and install the new listener
                _listener = new CppUnitTestReporter();
                listeners.Append(_listener);
            }
    
            // Reset any previous failures detected by the listener
            void ResetGoogleMock()
            {
                _listener->ResetFailures();
            }
    
            // Prints messages and asserts if any Google Mock messages are found.
            void CheckGoogleMock()
            {
                Assert::IsNotNull(_listener, L"Google Mock framework not initialized by InitGoogleMock()");
                _listener->CheckFailures();
            }
        }
    }
    

    I use the GMockUtils functions in unit test classes like this:

    #include "GMockUtils.h"
    
    using namespace Microsoft::VisualStudio::CppUnitTestFramework;
    using namespace ::testing;
    
    namespace GMockUtilsDemo
    {
        TEST_CLASS(GMockUtilTests)
        {
        public:
    
            TEST_CLASS_INITIALIZE(ClassInitializer)
            {
                // IMPORTANT: This must be called before any mock object constructors
                GMockUtils::InitGoogleMock();
            }
    
            TEST_METHOD_CLEANUP(MethodCleanup)
            {
                // Checks for GoogleMock messages. Asserts if found.
                GMockUtils::CheckGoogleMock();
            }
    
            TEST_METHOD(Method1_ParamIsOne_Method2CalledWithOne)
            {
                MockTestClass mockObj;
                EXPECT_CALL(mockObj, Method2(1)).Times(1); // Expectation will be met
                mockObj.Method1(1);
            }
    
            TEST_METHOD(Method1_ParamIsZero_IntentionallyFail)
            {
                MockTestClass mockObj;
                EXPECT_CALL(mockObj, Method2(1)).Times(1); // Expectation will not be met
                mockObj.Method1(0);
            }
        };
    }
    


    The Console Output

    The console output now shows all GMock messages, and the run does not abort on the first test failure.

    [3/27/2019 12:23:46 PM Informational] ------ Run test started ------
    [3/27/2019 12:23:46 PM Informational] 
    Unexpected mock function call - returning directly.
        Function call: Method2(0)
    Google Mock tried the following 1 expectation, but it didn't match:
    
    c:\...\gmockutilsdemo.cpp(64): EXPECT_CALL(_mockObj, Method2(1))...
      Expected arg #0: is equal to 1
               Actual: 0
             Expected: to be called once
               Actual: never called - unsatisfied and active
    [3/27/2019 12:23:46 PM Informational] Actual function call count doesn't match EXPECT_CALL(_mockObj, Method2(1))...
             Expected: to be called once
               Actual: never called - unsatisfied and active
    [3/27/2019 12:23:46 PM Informational] ========== Run test finished: 2 run (0:00:00.8631468) ==========
    


    Test Explorer View

    If I run the tests via Visual Studio Test Explorer, I can also see all GMock messages related to a particular test. It also works with the VsTest task on Azure DevOps.

    Test Explorer screen snippet

    Hopefully this will be useful to anyone who finds themselves in the same situation.