如何为二维数组编写一个好的 GMock 匹配器?
How to write a good GMock matcher for two-dimensional arrays?
我的 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 功能来使这个匹配器更好吗?或者也许有人可以指出我应该更仔细阅读的文档的一部分,以了解我在这里可能遗漏了什么?
您想获取有关索引的信息,但似乎不想获取实际值。怎么样:
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;
}
您可能会考虑的一件事是显式 return false
或 true
形成匹配器而不是调用断言。然后,您可以使用 result_listener
提供有关比赛中到底出了什么问题的附加信息。您还应该在执行检查之前检查数组维度以避免未定义的行为
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 of
和 Expected
反馈字段中打印 std::array
的内容。在 docs 中他们提到了 void PrintTo
功能,但它对我不起作用。
=== 编辑 ===
如果您可以创建一个二维数组 class 而不是 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
class 字段而不是直接使用 operator[]
和 size()
。或者您可以为 class.
重载它们
如果 @JanHackenberg 的评论是你想要的,在你的匹配器中只需设置标志 result = false
而不是 return
(不过我不会这样做,因为对于大数组它不会可读)。
我的 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 功能来使这个匹配器更好吗?或者也许有人可以指出我应该更仔细阅读的文档的一部分,以了解我在这里可能遗漏了什么?
您想获取有关索引的信息,但似乎不想获取实际值。怎么样:
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;
}
您可能会考虑的一件事是显式 return false
或 true
形成匹配器而不是调用断言。然后,您可以使用 result_listener
提供有关比赛中到底出了什么问题的附加信息。您还应该在执行检查之前检查数组维度以避免未定义的行为
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 of
和 Expected
反馈字段中打印 std::array
的内容。在 docs 中他们提到了 void PrintTo
功能,但它对我不起作用。
=== 编辑 ===
如果您可以创建一个二维数组 class 而不是 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
class 字段而不是直接使用 operator[]
和 size()
。或者您可以为 class.
如果 @JanHackenberg 的评论是你想要的,在你的匹配器中只需设置标志 result = false
而不是 return
(不过我不会这样做,因为对于大数组它不会可读)。