背景
我正在開發一個與各種相機及其獨特 API 介面的專案。所有相機都被抽象為一個介面,相機特定的 API 呼叫包含在它們自己的實作類中。
問題說明
我正在嘗試測驗一種決議配置形式并回傳指向新創建的相機的指標的工廠方法。這是它的外觀的高級視圖:
// camerafactory.cpp
Camera* make_camera(Config& config) {
switch (config.type) {
case CameraTypes.A : {
int paramInt = config.paramInt;
return new CameraA(paramInt);
}
case CameraTypes.B : {
string paramString = config.paramString;
return new CameraB(paramString);
}
case CameraTypes.C : {
float param1 = config.param1;
float param2 = config.param2;
return new CameraC(param1, param2);
}
default: {
throw std::invalid_argument("Invalid configuration!");
}
}
}
我想測驗這些建構式是否被正確呼叫,但是這需要將相機物理連接到計算機,以允許相機建構式中的相機 API 作業。這對于某些測驗情況確實有意義,即在回圈中演示硬體,但是在我的特定情況下,同時連接所有型別的相機是不現實的。
我想測驗這些建構式至少被正確的引數呼叫,證明決議作業正常。我曾考慮將決議和測驗分別抽象出來,但從技術上講,仍然有未測驗的建構式呼叫。我也總是被告知,為了測驗而添加功能通常是應該避免的,所以這是我在解決方案中考慮的另一件事。找到一種解決方案也很有趣,該解決方案允許在模擬/具體實作之間輕松進行編譯時切換以測驗回圈中的硬體。
版本
請注意,我使用的是 C 17、CMake 和最新版本的 gtest。
正在考慮的解決方案
使用宏將建構式呼叫更改為 Mock 建構式呼叫。
在 的頂部camerafactory.cpp,添加如下內容:
// camerafactory.cpp
#ifndef PRODUCTION
#include "MockCamera.hpp"
#define CameraA MockCameraA
#define CameraB MockCameraB
#define CameraC MockCameraC
#endif
WhereMockCameraN在具有相同建構式的單獨 hpp/cpp 檔案中定義,并且還實作了相同的相機介面,然后我可以使用它來測驗正確的構造。
這將允許我測驗建構式是否被正確呼叫,但確實感覺它與可以使用 Java 之類的語言完成的更清晰的解決方案背道而馳。
使用轉基因
我還沒有深入研究它,但 GMock 似乎很有希望:http : //google.github.io/googletest/gmock_cook_book.html
uj5u.com熱心網友回復:
我有和沒有 gmock 的建議是使用可以在測驗代碼中覆寫的虛擬方法。
首先,將您的工廠函式 , 轉換make_camera為它自己的類,其中包含您要驗證的內容的虛擬方法。為簡潔起見,我使用的是行內方法。在生產代碼中,您可能希望將其分離為類宣告的 .h 檔案和實作的 .cpp 檔案。
class CameraFactory
{
public:
virtual Camera* make_camera_a(int param) {
return new CameraA(param);
}
virtual Camera* make_camera_b(const string& param) {
return new CameraB(param);
}
virtual Camera* make_camera_c(double param1, param2) {
return new CameraC(param1, param2);
}
virtual Camera* make_camera(const Config& config) {
switch (config.type) {
case CameraTypes::A: {
return make_camera_a(config.intParam);
}
case CameraTypes::B: {
return make_camera_b(config.strParam);
}
case CameraTypes::C: {
return make_camera_c(config.param1, config.param2);
}
default: {
throw std::invalid_argument("Invalid configuration!");
}
}
}
};
在您的產品代碼中,您通常會在其中呼叫make_camera,您可以只使用堆疊外的 CameraFactory:
在您的產品代碼中替換這樣的行:
Camera* cam = make_camera(config); // old code
有了這個:
CameraFactory factory;
Camera* cam = factory.make_camera(config);
沒有 gmock 的測驗:
舊的測驗方法是讓您的單元測驗覆寫類中的各個 make 函式,該類部分模擬了您想要驗證正確呼叫的虛擬方法。
class MockCamera : public Camera
{
};
class MockCameraFactory : public CameraFactory
{
virtual Camera* make_camera_a(int param) {
_camera_a_created = true;
_intParm = param;
return new MockCamera();
}
virtual Camera* make_camera_b(const string& param) {
_camera_b_created = true;
_strParam = param;
return new MockCamera();
}
virtual Camera* make_camera_c(double param1, param2) {
_camera_c_created = true;
_param1 = param1;
_param2 = param2;
return new MockCamera();
}
bool _camera_a_created;
bool _camera_b_created;
bool _camera_c_created;
int _intParam;
string _strParam;
double _param1;
double _param2;
CameraFactoryTest() {
resetState();
}
void resetState() {
_camera_a_created = false;
_camera_b_created = false;
_camera_c_created = false;
_intParam = 0;
_strParam = "";
_param1 = 0;
_param2 = 0;
}
};
請注意, CameraFactoryTest 不會覆寫make_camera,但它確實繼承自它。
然后您的單元測驗代碼可以驗證正確的引數是否正在傳遞給您的建構式。我只是猜測你的 ut 框架有一些名為UNIT_TEST_ASSERT. 我相信你有一些等價的東西。
void test_make_camera()
{
MockCameraFactory factory;
Config config;
// test camera A
config.type=CameraConfig::A;
config.intParam = 42;
factory.make_camera(config);
UNIT_TEST_ASSERT(factory._camera_a_created == true);
UNIT_TEST_ASSERT(factory._camera_b_created == false);
UNIT_TEST_ASSERT(factory._intParam == 42);
// test camera B
factory.resetState();
config = {};
config.type=CameraConfig::B;
config.strPara = "USB1";
factory.make_camera(config);
UNIT_TEST_ASSERT(factory._camera_b_created == true);
UNIT_TEST_ASSERT(factory._strParam == "USB1");
...
}
使用 Google 測驗 (GMock)
使用 gmock 甚至更容易,并使您的模擬類在其他測驗中更可用。您使用 GMock 方式,而不是顯式編碼 MockCameraFactory 類。
首先宣告一個從實際類繼承的模擬類。
class MockCameraFactory : public CameraFactory
{
public:
MOCK_METHOD1(make_camera_a, Camera*(int param));
MOCK_METHOD1(make_camera_b, Camera*(const string& param));
MOCK_METHOD2(make_camera_c, Camera*(const string& param));
};
然后你的測驗代碼可以做這樣的事情:
class test_make_camera()
{
MockCameraFactoryTest factory;
MockCamera cameramock;
EXPECT_CALL(factory, make_camera_a(42)).WillOnce(Return(&cameramock));
Config config = {};
config.type = CameraType::A;
config.intParam = 42;
factory.make_camera(config);
Mock::VerifyAndClearExpectations(&factory);
}
我正在瀏覽有關如何設定 Google 測驗的詳細資訊,以便它通過您自己的單元測驗框架進行斷言。但是你明白了。
還要注意 - 我最初make_camera在 CameraFactory 類中宣告為虛擬的。它實際上不必是虛擬的,因為您的測驗只是驗證make_camera_x方法是否被正確呼叫。
轉載請註明出處,本文鏈接:https://www.uj5u.com/qukuanlian/398974.html
