GTest中的嵌套匹配器

5

我想在其他匹配器中使用一些现有的匹配器。我知道MatcherInterface解决方案,但我想知道能否使用由MATCHER_P定义的匹配器。我找到了以下解决方案:

struct Foo
{
    double x;
    double y;
};

struct Bar
{
    Foo foo;
    int i;
};

MATCHER_P(EqFoo, foo, "")
{
    ::testing::Matcher<double> x_matcher = ::testing::DoubleNear(foo.x, 0.0001);
    if (!x_matcher.MatchAndExplain(arg.x, result_listener))
    {
        return false;
    }

    ::testing::Matcher<double> y_matcher = ::testing::DoubleNear(foo.y, 0.0001);
    if (!y_matcher.MatchAndExplain(arg.y, result_listener))
    {
        return false;
    }

    return true;
}

MATCHER_P(EqBar, bar, "")
{
    ::testing::Matcher<Foo> foo_matcher = EqFooMatcherP<Foo>(bar.foo);

    if (!foo_matcher.MatchAndExplain(arg.foo, result_listener))
    {
        return false;
    }

    if (bar.i != arg.i)
    {
        return false;
    }

    return true;
}

TEST_F(TestClass, BarTest)
{
    Bar bar_val{{10.12, 76.43}, 78};
    Bar bar_exp{{10.12, 99.99}, 78};

    EXPECT_THAT(bar_val, EqBar(bar_exp));
}

我在想,是否有更好、更好的解决方案来

  • 在另一个匹配器中使用我的 MATCHER_P 匹配器
  • 在另一个匹配器中使用原始 GTest 匹配器。

你的测试是一个例子吗?否则你将不需要匹配器。 - Tobias Wollgam
当然,这只是我原始代码的简化版。 - Tibor Takács
1个回答

10

正确的方式是尽可能使用来自gtest/gmock的匹配器。只有在没有已提供的匹配器时才使用自己的。

在您的示例中,就像这样简单:

auto EqFoo(const Foo& expected)
{
   return ::testing::AllOf(
         ::testing::Field(&Foo::x, ::testing::DoubleNear(expected.x, 0.0001)),
         ::testing::Field(&Foo::y, ::testing::DoubleNear(expected.y, 0.0001))
     );
}

auto EqBar(const Bar& expected)
{
   return ::testing::AllOf(
         ::testing::Field(&Bar::foo, EqFoo(expected.foo)),
         ::testing::Field(&Bar::i, expected.i)
     );
}

更一般的方法是使用重载:

auto MatchDouble(double expected)   
{
    return ::testing::DoubleNear(expected.x, 0.0001);
}
auto MatchFoo(::testing::Matcher<double> x, ::testing::Matcher<double> y)
{
   return ::testing::AllOf(
         ::testing::Field(&Foo::x, x),
         ::testing::Field(&Foo::y, y)
     );
}
auto MatchFoo(double x, double y)
{
   return MatchFoo(MatchDouble(x), MatchDouble(y));
}

auto MatchBar(::testing::Matcher<Foo> foo, ::testing::Matcher<int> i)
{
   return ::testing::AllOf(
         ::testing::Field(&Bar::foo, foo),
         ::testing::Field(&Bar::i, expected.i),
     );
}
auto MatchBar(const Bar& expected)
{
   return MatchBar(expected.foo, expected.i);
}

那么,您的测试:

TEST_F(TestClass, BarTest)
{
    Bar bar_val{{10.12, 76.43}, 78};
    Bar bar_exp{{10.12, 99.99}, 78};

    EXPECT_THAT(bar_val, MatchBar(bar_exp));

    // or - e.g. you can match only Bar::y if other things are irrelevant in your test
    EXPECT_THAT(bar_val, MatchBar(MatchFoo(_, MatchDouble(2.001)), _);
}

无论如何 - 使用 MATCHER_P 应该是相当罕见的情况,我的观察是这个宏确实被过度使用了。
如果您的项目是 C++14 之前的版本,请在这些函数的返回类型中使用 ::testing::Matcher<T> 而不是 auto

如果您正在使用模拟,并将结构体作为指针传递,请不要忘记执行Pointee(EqFoo(...)),否则您将会得到一些奇怪的编译器错误。 - T'n'E
有没有一种方法可以在字段/属性和匹配的值之间执行类型转换?我有一个场景,需要进行额外的API调用来将其转换为std :: string / C样式字符串,以便能够调用各种字符串匹配器gmock具有(例如HasSubstr)。或者是自定义匹配器,将从myString到std :: string的转换,以便我可以使用HasSubstr匹配器。这可能吗? - Arcin B
@ArcinB,你可以通过MATCHER_P宏创建自己的MyHasSubstr匹配器,在其中调用HasSubstr。或者你可以查看gmock文档,看看它们是否有任何“转换匹配器”——最近我检查时那里没有这样的东西。 - PiotrNycz
MATCHER_P 是我也实现的内容。@PiotrNycz。但是有一些答案在这里和其他地方推荐默认匹配器的功能并将它们组合起来。不需要指向新的匹配器实现,这样做更好。基本需求是在进行匹配之前将结果转换。gmock没有提供这样的功能吗? - Arcin B
@ArcinB 正如我所写的 - 没有转换值匹配器。你可以编写这样的匹配器 - 如 template <typename Functor> class ConvertingMatcher ...;} 对于更详细的建议,您需要提出问题 - 作为一个问题 - 而不是对另一个问题的评论。 - PiotrNycz

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接