如何将std::function作为参数传递给gmock中的mocked方法?

问题描述 投票:1回答:1

当我试图传递一个 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; }
c++ googletest gmock
1个回答
1
投票

有一个预先定义的 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)。


0
投票

中命名的方法的参数。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。否则,你可以为每个不同的虚回调指定唯一的键。

© www.soinside.com 2019 - 2024. All rights reserved.