当我试图传递一个 std:function
中的一个模拟方法的参数。EXPECT_CALL
.我想验证一下 Bar
被称为 callback
作为一个参数。
代码。
#include <gtest/gtest.h>
#include <gmock/gmock.h>
class Foo {
public:
virtual void Bar(const std::function<void (const std::string &name)> &callback) = 0;
};
class MockFoo : public Foo {
public:
MOCK_METHOD(void, Bar, (const std::function<void (const std::string &name)> &callback));
};
TEST(FooBarTest, callback) {
MockFoo foo;
const std::function<void(const std::string &name)> callback;
EXPECT_CALL(foo, Bar(callback)).Times(1);
foo.Bar(callback);
}
int main(int argc, char **argv) {
testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
在运行时产生了一个错误。
/usr/local/include/gtest/gtest-matchers.h:211:60: error: no match for ‘operator==’ (operand types are ‘const std::function<void(const std::__cxx11::basic_string<char>&)>’ and ‘const std::function<void(const std::__cxx11::basic_string<char>&)>’)
bool operator()(const A& a, const B& b) const { return a == b; }
有一个预先定义的 ACTIONS
你可以用于此目的。您的 EXPECT_CALL
会是这样的。
using namespace testing; // for brevity
TEST(FooBarTest, callback) {
MockFoo foo;
const std::function<void(const std::string &name)> callback;
EXPECT_CALL(foo, Bar(_)).WillOnce(Invoke(callback));
foo.Bar(callback);
}
正如@IanGralinski所指出的那样,没有匹配器可以用于 std::function
,所以你可以使用任何匹配器进行调用(如 _
).
然而,这不是我在这里使用gmock的方式--为什么要嘲讽 Foo
如果你直接使用它?通常mocks是用来测试你的(真实)类与其他类(mocked)的交互的。所以在你的例子中。Foo
可以通过 MockFoo
并被其他类(真实类)使用,使用依赖注入。
顺便说一下,记得把虚拟析构器添加到 Foo
如果从 Foo
的指针来删除。Foo
(这将是没有虚拟dtor的UB)。
中命名的方法的参数。EXPECT_CALL
实际上是匹配器。当你只是提供了一个值 (一些还不是 gmock 的东西) Matcher<T>
类型),这意味着一个 Eq
匹配器。所以 EXPECT_CALL(foo, Bar(callback))
真意 EXPECT_CALL(foo, Bar(Eq(callback)))
. 问题是, std::function
不提供 operator==
用于比较两个函数。它的类型擦除属性意味着平等测试在一般情况下是不可能实现的,当然它可能包裹的一些类漏斗类型也不会有自己的 operator==
也不是。
但可以检验是否有 std::function
包含一个非常具体的对象。如果你不想仅仅通过期待 Bar(_)
这里有一个辨别是否是一个 std::function
是一个特定的虚函数。
首先,创建一个可调用的类,我们将用它来初始化我们的 std::function
对象。我也会有它的 operator()
调用一个mock方法,当它只是传递给一个mock函数时,是不需要的,但这将使它可以使用同样的 std::function
在不同的上下文中,或者在一个真实的函数前设置期望值,既可以直接调用它,也可以将它传递给一个mocked接口。
template <typename FuncT> class DummyFunction; // undefined
template <typename RetType, typename... ArgTypes>
class DummyFunction<RetType(ArgTypes...)> {
public:
constexpr DummyFunction() : DummyFunction(0) {}
explicit constexpr DummyFunction(int key) : m_key(key) {}
constexpr DummyFunction(const DummyFunction& f) : m_key(f.m_key) {}
constexpr int key() const { return m_key; }
MOCK_METHOD(RetType, doCall, (ArgTypes...));
RetType operator()(ArgTypes... args)
{ return doCall(std::forward<ArgTypes>(args)...); }
friend constexpr bool operator==(const DummyFunction& f1, const DummyFunction& f2)
{ return f1.m_key == f2.m_key; }
friend constexpr bool operator!=(const DummyFunction& f1, const DummyFunction& f2)
{ return !(f1 == f2); }
friend std::ostream& operator<<(std::ostream& os, const DummyFunction& f)
{ return os << "DummyFunction(" << f.m_key << ")"; }
private:
int m_key;
};
然后用一个gmock Matcher来测试是否有一个 std::function
含有 DummyFunction
对象,其函数类型与模板参数完全相同,且键值与给定的参数相同。DummyFunction
对象可以是这样的。因为它可以将一种类型的 std::function
到另一个,只要参数类型和返回类型转换正确(或者返回类型改变为 void
),我把它做成了一个 "多态 "的匹配器,它可以接受任何的 std::function
测试的专业化。
template <class DummyFuncType>
class IsDummyFunctionTester {
public:
explicit constexpr IsDummyFunctionTester(int key) : m_key(key) {}
// The three member functions required for gmock "PolymorphicMatcher":
template <typename FuncType>
bool MatchAndExplain(const std::function<FuncType>& f,
::testing::MatchResultListener* listener) const {
bool type_ok = f.target_type() == typeid(DummyFuncType);
if (type_ok) {
int f_key = f.template target<DummyFuncType>()->key();
if (f_key == m_key) return true;
*listener << "std::function contains DummyFunction(" << m_key << ")";
} else if (!f) {
*listener << "std::function is empty";
} else {
// Note name() is implementation dependent. For g++/clang it's mangled.
*listener << "std::function target's type_info::name() is "
<< f.target_type().name();
}
return false;
}
void DescribeTo(std::ostream* os) const
{ *os << "is a DummyFunction(" << m_key << ")"; }
void DescribeNegationTo(std::ostream* os) const
{ *os << "is not a DummyFunction(" << m_key << ")"; }
private:
int m_key;
};
template <typename FuncType>
decltype(auto) StdFuncIsDummyFunc(const DummyFunction<FuncType>& f) {
return ::testing::MakePolymorphicMatcher(
IsDummyFunctionTester<DummyFunction<FuncType>>(f.key()));
}
所以最后,你可以做。
TEST(FooBarTest, callback) {
MockFoo foo;
const DummyFunction<void(const std::string &name)> callback;
EXPECT_CALL(foo, Bar(StdFuncIsDummyFunc(callback))).Times(1);
foo.Bar(callback);
}
如果你只有一个 DummyFunction
,或者到底是哪个 DummyFunction
对测试来说并不重要,你可以像上面一样使用默认的 "key "为0。否则,你可以为每个不同的虚回调指定唯一的键。