Аргументы метода, указанного в EXPECT_CALL
, на самом деле являются сопоставителями. Когда вы просто указываете значение (что-то, что еще не относится к типу gmock Matcher<T>
), это подразумевает сопоставление Eq
. Итак, EXPECT_CALL(foo, Bar(callback))
на самом деле означает EXPECT_CALL(foo, Bar(Eq(callback)))
. Проблема в том, что std::function
не предоставляет operator==
для сравнения двух функций. Его свойства стирания типов означают, что проверку на равенство невозможно реализовать в целом, плюс, конечно, некоторые из типов функторов классов, которые он может обернуть, также не будут иметь своих operator==
.
Но это так. можно проверить, содержит ли std::function
очень специфический объект c. Если вы не хотите просто игнорировать аргумент, ожидая Bar(_)
, вот идея для определения того, является ли std::function
фиктивной функцией c спецификацией.
Сначала создайте вызываемый класс, который мы будет использовать для инициализации объекта std::function
. У меня также будет его operator()
вызов фиктивного метода, который не нужен, когда он просто передается в фиктивную функцию, но это позволит использовать один и тот же std::function
в разных контекстах или установить ожидания до одного реального функция, которая будет вызывать его напрямую и передавать в имитируемый интерфейс.
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
), я сделал его сопоставителем "polymorphi c", который принимает любые 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
какое не имеет значения для теста, вы можете просто использовать «ключ» по умолчанию, равный нулю, как указано выше. В противном случае вы можете указать уникальные ключи для каждого отдельного фиктивного обратного вызова.