Как использовать Google Mock с CppUnitTestFramework - PullRequest
0 голосов
/ 27 марта 2019

TL; DR: Вы можете использовать GMock, чтобы добавить возможность имитации в свои собственные модульные тесты c ++ Microsoft.Подробности см. В моем ответе ниже.


Я хочу начать добавлять макеты в мой существующий набор собственных модульных тестов.Тесты написаны с использованием Microsoft CppUnitTestFramework фреймворка, который не поддерживает насмешки.Я на самом деле не хочу преобразовывать весь набор тестов в другую инфраструктуру, просто чтобы добавить несколько макетов.

Фреймворк Google GMock , кажется, предоставляет все, что мне нужно, и документация предполагает, что это может бытьиспользуется с каркасами, отличными от gtest.Поэтому, используя советы из постов в блоге, такие как , этот , я создал пару юнит-тестов.

    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);
        }

    };

Результаты менее чем удовлетворительные.Ожидания работают (первый метод проходит), но если какое-либо ожидание не выполняется, весь запуск прерывается, и в выводе теста отображается только следующее бесполезное сообщение:

[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) ==========

Окно Visual Studio Test Explorer не указываетв чем проблема тоже.Это просто показывает, что один тест пройден успешно, а другой не запущен:

Test Explorer screen snip

Итак, что я ищу из этой интеграции:

  1. Сбой теста GMock не прерывает весь прогон.
  2. Тесты, которые не соответствуют ожиданиям GMock, отображаются как неудачные.
  3. Все сообщения GMock должны отображаться втестовый вывод.

1 Ответ

0 голосов
/ 27 марта 2019

Я в итоге смог заставить GMock работать правильно с CppUnitTestFramework. Затем я создал простой набор интерфейсных функций, чтобы с ним было легче работать.

Я использовал пакет NuGet gmock 1.7.0 для установки инфраструктуры GMock в свой проект, затем я добавил эти два файла в свой проект:

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);
        }
    }
}

Я использую три функции GMockUtils в классах модульного тестирования следующим образом:

#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);
        }
    };
}


Консольный вывод

Вывод консоли теперь показывает все сообщения GMock, и запуск не прерывается при первом неудачном тесте.

[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

Если я запускаю тесты через Visual Studio Test Explorer, я также вижу все сообщения GMock, относящиеся к определенному тесту. Он также работает с задачей VsTest в DevOps Azure.

Test Explorer screen snippet

Надеюсь, это будет полезно всем, кто окажется в такой же ситуации.

...