在Google UnitTest中检测glog输出

Detecting a glog output in a Google UnitTest

本文关键字:glog 输出 检测 Google UnitTest      更新时间:2023-10-16

以下是我在源文件中的一个函数

cv::Mat* Retina::Preprocessing::create_mask(const cv::Mat *img, const uint8_t threshold) {
LOG_IF(ERROR, img->empty()) << "The input image is empty. Terminating Now!!";
cv::Mat green_channel(img->rows(), img->cols(), CV_8UC1); /*!< Green channel. */
/// Check number of channels in img and based on that find out the green channel.
switch (img->channels()){
/// For 3 channels, it is an RGB image and we use cv::mixChannels().
case(3): {
int from_to[] =  {1,0};
cv::mixChannels(img, 1, &green_channel, 1, from_to, 1);
break;
}
/// For a single channel, we assume that it is the green channel.
case(1): {
green_channel = *img;
break;
}
/// Otherwise we are out of clue and throw an error.
default: {
LOG(ERROR)<<"Number of image channels found = "<< img->channels()<<". Terminating Now!!";
return nullptr; /*!< (unreachable code) Only given for completion */
}
}
cv::Mat mask(img->rows(), img->cols(), CV_8UC1);/*!< Empty mask image */
if (threshold == 0)/// Otsu's threshold is used
cv::threshold(green, mask, threshold, 255, CV_THRESH_BINARY | CV_THRESH_OTSU);
else /// We can use the provided threshold value
cv::threshold(green, mask, threshold, 255, CV_THRESH_BINARY);
LOG_IF(ERROR, mask.empty())<< "After thresholding, image became empty. Terminating Now!!";

std::vector<std::vector<cv::Point>> contours;
/// Get the contours in the binary mask.
cv::findContours(mask, *contours, CV_RETR_LIST, CV_CHAIN_APPROX_NONE);
size_t max_index{};
double prev_area{};
size_t index{};
/// Lambda function for a convenient one-liner std::for_each. SEE BELOW.
lambda_max_index = [max_index, prev_area, index] (std::vector<cv::Point> c){
double new_area { cv::contourArea(c) };
max_index = (new_area > prev_area) ? max_index = index : max_index;
++index;
};
/// For each contour compute its area, and over the loop find out the largest contour.
std::for_each(contours.begin(), contours.end(), lambda_max_index());
/// Iterate over each point and test if it lies inside the contour, and drive in it.
for(size_t row_pt = 0; row_pt < mask_out.rows(); ++row_pt){
for(size_t col_pt = 0; col_pt < mask_out.cols(); ++col_pt){
if (cv::pointPolygonTest(contours[max_index], cv::Point(row_pt, col_pt))>=0)
mask[row_pt, col_pt] = 255;
else
mask[row_pt, col_pt] = 0;
}
}
return &mask;
}

下面是我写的一个谷歌单元测试文件。这是我第一次尝试GoogleTest。

#include"PreProcessing.hxx"
#include<opencv2/highgui/highgui.hpp>
#include<gtest/gtest.h>
TEST(Fundus_Mask_Creation, NO_THRESHOLD) {
cv::Mat img(cv::imread(std::string("../data/test_img.jpg")));
cv::Mat* mask = Retina::Preprocessing::create_mask(&img);
ASSERT_TRUE(!(mask->empty()));
std::string winname("Input Image");
cv::namedWindow(winname);
cv::imshow(winname, img);
cv::waitKey(800);
cv::destroyWindow(winname);
winname = "Fundus Mask";
cv::namedWindow(winname);
cv::imshow(winname, *mask);
cv::waitKey(800);
}
TEST(Fundus_Mask_Creation, THRESHOLD) {
cv::Mat img(cv::imread(std::string("../data/test_img.jpg")));
std::uint8_t threshold{10};
cv::Mat* mask = Retina::Preprocessing::create_mask(&img, threshold);
ASSERT_TRUE(!(mask->empty()));
std::string winname("Input Image");
cv::namedWindow(winname);
cv::imshow(winname, img);
cv::waitKey(800);
cv::destroyWindow(winname);
winname = "Fundus Mask";
cv::namedWindow(winname);
cv::imshow(winname, *mask);
cv::waitKey(800);
}

我还想测试一下glog记录"Number of image channels found = "<< img->channels()<<". Terminating Now!!!";的情况

当我向它提供一个输入,从而正确记录上述消息时,我如何编写一个单元测试,它将成功运行(即原始源文件将记录FATAL错误)?

您需要mocking(googlemock)来实现这一点。要启用mocking,请为glog调用创建一个精简包装类,以及该类将从中继承的接口(您需要这样才能启用mocking)。您可以将此作为指导原则:

class IGlogWrapper {
public:
...
virtual void LogIf(int logMessageType, bool condition, const std::string &message) = 0;
...
};
class GlogWrapper : public IGlogWrapper {
public:
...
void LogIf(int logMessageType, bool condition, const std::string &message) override {
LOG_IF(logMessageType, condition) << message;
}
...
};

现在创建一个模拟类:

class GlogWrapperMock : public IGlogWrapper {
public:
...
MOCK_METHOD3(LogIf, void(int, bool, const std::string &));
...
};

并使您的Retina::Preprocessing类持有指向IGlogWrapper的指针,并通过该接口进行日志记录调用。现在,您可以使用依赖注入将指向将使用glog的真实记录器类GlogWrapper的指针传递给Retina::Preprocessing类,而在测试中,您可以将指向模拟对象(GlogWrapperMock的实例)的指针传递。通过这种方式,您可以在具有两个通道的测试中创建一个映像,并在此模拟对象上设置期望值,以便在这种情况下调用函数LogIf。以下示例使用公共方法注入要使用的记录器:

using namespace testing;
TEST(Fundus_Mask_Creation, FAILS_FOR_TWO_CHANNEL_IMAGES) {
// create image with two channels
cv::Mat img( ... );
GlogWrapperMock glogMock;
Retina::Preprocessing::setLogger(&glogMock);
std::string expectedMessage = "Number of image channels found = 2. Terminating Now!!";
EXPECT_CALL(glogMock, LogIf(ERROR, _, expectedMessage).WillOnce(Return());
cv::Mat* mask = Retina::Preprocessing::create_mask(&img);
// Make additional assertions if necessary
...
}

有关模拟的更多信息,请查看文档:https://github.com/google/googletest/blob/master/googlemock/docs/ForDummies.md#getting-已启动此外,请注意,您发布的两个单元测试没有多大意义,因为您正在从磁盘加载图像,并在断言后进行额外调用。单元测试应该简洁、快速并且与环境隔绝。我建议你多读一些关于单元测试的书,互联网上有很多资源。希望这能有所帮助!