如何为二维数组编写一个好的GMock匹配器



我的C++代码需要一组有限的二维矩阵代数运算。我决定使用std::array来实现这一点,如下所示:

template <typename T, size_t N, size_t M>
using array_2d = std::array<std::array<T, M>, N>;

我应该如何正确地为这种类型编写一个GMock匹配器,以比较两个这样的二重矩阵?我想出了一个不那么聪明的:

MATCHER_P(Arrays2dDoubleEq, expected, "") {
for (int i = 0; i < arg.size(); i++) {
for (int j = 0; j < arg[i].size(); j++) {
EXPECT_THAT(arg[i][j], DoubleEq(expected[i][j]));
}
}
return true;
}
MATCHER_P2(Arrays2dDoubleNear, expected, max_abs_err, "") {
for (int i = 0; i < arg.size(); i++) {
for (int j = 0; j < arg[i].size(); j++) {
EXPECT_THAT(arg[i][j], DoubleNear(expected[i][j], max_abs_err));
}
}
return true;
}

我使用的是:EXPECT_THAT(result, Arrays2dDoubleEq(expected));

这不仅看起来很难编码,而且没有给出很好的反馈。当矩阵不匹配时,失败断言的输出是一堆不相等的二重。失败的测试输出很难阅读,并且缺少关于矩阵索引的信息。

我觉得可以(也应该(用更好的方式来做。我已经看了一些文档和GMock食谱。虽然有一些容器匹配器,但我不能同时使用它们来比较两个嵌套数组。

有人能指出我应该使用哪些GMock功能来使这个匹配器更好吗?或者,也许有人可以指出我应该更仔细阅读的文件的一部分,以了解我在这里可能遗漏了什么?

您可能会考虑的一件事是从匹配器显式返回falsetrue,而不是调用断言。然后,您可以使用result_listener来提供有关比赛中到底出了什么问题的其他信息。在执行检查之前,您还应该检查阵列的尺寸,以避免未定义的bahavior

using testing::DoubleEq;
using testing::Value;
using testing::Not;
MATCHER_P(Arrays2dDoubleEq, expected, "") {
if (arg.size() != expected.size())
{
*result_listener << "arg.size() != expected.size() ";
*result_listener << arg.size() << " vs " << expected.size();
return false;
}
for (size_t i = 0; i < arg.size(); i++) {
if (arg[i].size() != expected[i].size())
{
*result_listener << "arg[i].size() != expected[i].size() i = " << i << "; ";
*result_listener << arg[i].size() << " vs " << expected[i].size();
return false;
}
for (size_t j = 0; j < arg[i].size(); j++) {
if (!Value(arg[i][j], DoubleEq(expected[i][j])))
{
*result_listener << "element(" << i << ", " << j << ") mismatch ";
*result_listener << arg[i][j] << " vs " << expected[i][j];
return false;
}
}
}
return true;
}
TEST(xxx, yyy)
{
array_2d<double, 2, 3> arr1 = {std::array<double, 3>({1, 2, 3}), std::array<double, 3>({4, 5, 6})};
array_2d<double, 2, 3> arr2 = arr1;
array_2d<double, 2, 3> arr3 = arr1;
arr3[0][0] = 69.69;
array_2d<double, 5, 6> arr4;
ASSERT_THAT(arr1, Arrays2dDoubleEq(arr2));
ASSERT_THAT(arr2, Not(Arrays2dDoubleEq(arr3)));
ASSERT_THAT(arr2, Not(Arrays2dDoubleEq(arr4)));
}

不幸的是,我还不知道如何告诉gmock不要在Value ofExpected反馈字段中打印std::array的内容。在文档中,他们提到了void PrintTo功能,但它对我不起作用。

===编辑===

如果创建一个2D数组类而不是typedef是可以的,那么通过提供operator<<重载:可以很容易地抑制gmock混乱的输出

template <typename T, size_t N, size_t M>
struct Array2D
{
std::array<std::array<T, M>, N> data;
};
template <typename T, size_t N, size_t M>
std::ostream& operator<<(std::ostream& os, const Array2D<T, N, M>&)
{
os << "Array2D<" << typeid(T).name() << "," << N << "," << M << ">";
return os;
}

然后,您需要稍微修改匹配器以使用data类字段,而不是直接使用operator[]size()。或者你可以为你的班级超载。

如果@JanHackenberg的评论是你想要的,那么在你的匹配器中只需设置标志result = false而不是return(不过我不会这么做,因为对于大数组来说,它是不可读的(。

您想要获得有关索引的信息,但似乎不想获得实际值。怎么样:

MATCHER_P(Arrays2dDoubleEq, expected, "") {
bool allMatched = true;
for (int i = 0; i < arg.size(); i++) {
for (int j = 0; j < arg[i].size(); j++) {
if(arg[i][j] != expected[i][j]) {
std::cout << "Failing at indices:" << i << ";" << j << std::endl;
allMatched = false;
}
}
}
EXPECT_THAT(allMatched, true);
return true;
}

最新更新