Как использовать Google Mock с CppUnitTestFramework
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 также не указывает на проблему. Это просто показывает, что один тест пройден успешно, а другой не запущен:
Итак, что я ищу от этой интеграции:
- Сбой теста GMock не прерывает весь прогон.
- Тесты, которые не соответствуют ожиданиям GMock, показаны как провальные.
- Все сообщения GMock должны отображаться в результатах теста.
1 ответ
Я в конце концов смог получить GMock
правильно работает с CppUnitTestFramework
, Затем я создал простой набор интерфейсных функций, чтобы с ним было легче работать.
Я использовал пакет gmock 1.7.0 NuGet для установки фреймворка 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.
Надеюсь, это будет полезно всем, кто окажется в такой же ситуации.