How to use Google Mock with CppUnitTestFramework
Asked Answered
D

1

5

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.
Depressomotor answered 27/3, 2019 at 18:41 Comment(0)
D
7

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.

Depressomotor answered 27/3, 2019 at 18:41 Comment(2)
I used this as a guide to set up a simple example like you have above, and I get an access violation after the test completes. This happens regardless if the test passes or fails, and it only occurs if I use GMock in the test... did you run into this as well?Kettledrum
It seems tied to the presence of EXPECT_CALL, specifically. Tests which create a mock with no expectations do not trigger the crash.Kettledrum

© 2022 - 2024 — McMap. All rights reserved.