How to use Google Mock with CppUnitTestFramework

2019-08-20 01:35发布

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.

1条回答
虎瘦雄心在
2楼-- · 2019-08-20 02:23

I was eventually able to get GMock working correctly with the CppUnitTestFramework. 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, then I added these two files to my project:

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 clear any previous failures and expectations.
        void ResetGoogleMock();

        // Call once per test method to check GoogleMock expectations.
        void CheckGoogleMock(void *mockObj=nullptr);
    }
}

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()
        {
            int argc = 0;
            char** argv = nullptr;
            ::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();
        }

        // Asserts if any expectations fail to verify on the Mock object
        void CheckGoogleMock(void *mockObj)
        {
            Assert::IsNotNull(_listener, L"Google Mock framework not initialized by InitGoogleMock()");
            bool result = true;
            if (mockObj)
                result = Mock::VerifyAndClearExpectations(mockObj);
            _listener->CheckFailures();
            Assert::IsTrue(result);
        }
    }
}

I use the three 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:
        MockTestClass _mockObj;

        TEST_CLASS_INITIALIZE(ClassInitializer)
        {
            // IMPORTANT: This must be called before any mock object constructors
            GMockUtils::InitGoogleMock();
        }

        TEST_METHOD_INITIALIZE(MethodInitializer)
        {
            // Clean up any left over expectations from failed tests
            GMockUtils::ResetGoogleMock();
        }

        TEST_METHOD_CLEANUP(MethodCleanup)
        {
            // Check that expectations were met. Asserts if not.
            GMockUtils::CheckGoogleMock(&_mockObj);
        }

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

        TEST_METHOD(Method1_ParamIsZero_IntentionallyFail)
        {
            // Expectation will not be met
            EXPECT_CALL(_mockObj, Method2(1)).Times(1);
            _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.

查看更多
登录 后发表回答