如何检查是否调用了成员方法

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

我有一个类(让我们说:Parent)在Member对象中创建。

我想知道,我如何测试在父方法调用上调用该成员方法。

我们来看一个例子:

class Parent : public IParent
{
public:
    Member() : member{} {}
    void parent_method() override
    {
        member.member_method();
    }
private:
    Member member;
}

我想准备测试,检查是否使用GTest / GMock在parent_method调用上调用了member_method。

重要提示:我想避免多态调用或任何动态分配 - 如果可能的话。

我有想法在构造函数中传递一些Factory并构建正确的Member对象(真实或模拟),但它需要多态调用和动态分配。我想避免这种开销。

是否有任何方法以相当干净的方式做到这一点?

欢迎任何建议。提前致谢。

c++ unit-testing googletest gmock
1个回答
1
投票

解决方案是使用模板。以下是google mock的文档mock non-virtual methods using templates中建议的变体:

#include <iostream>
#include <gtest/gtest.h>
#include <gmock/gmock.h>

using namespace std;

// YOUR SUT
class IParent
{
public:
    virtual ~IParent() {};

    virtual void parent_method() = 0;
};

class Member
{
public:
    void member_method()
    {
        cout << "member_method\n";
    }
};

template <class T = Member>   // make it a template
class Parent : public IParent
{
public:
    Parent() : member{} {}
    void parent_method() override
    {
        member.member_method();
    }
private:
    using member_type = T;
    member_type member; // member is now of type T (i.e. member_type)

    template <class U>
    friend U& GetMemberForTestMock(Parent<U>& parent); // necessary to set
                                                       // mock expectations
                                                       // for private member
};


// code in the test/mock file as this function is only for
// testing with mockMembers
template <class U>
U& GetMemberForTestMock(Parent<U>& parent)
{
    return parent.member;
}

// Your mock class
class MockMember
{
public:
    MOCK_CONST_METHOD0(member_method, void());
};


// Google Test
TEST(ParentTest, callsMemberMethodOnce)
{
    auto parent = Parent<MockMember>{};
    EXPECT_CALL(GetMemberForTestMock(parent), member_method()).Times(1);
    parent.parent_method();
}

您可以通过修改friend来消除Parent::Parent()语句,以便您可以将指针传递给Member或MockMember:

template <class T = Member>   // make it a template
class Parent : public IParent
{
public:
    using member_type = T;

    Parent(member_type* m) : member{m} {}
    void parent_method() override
    {
        member->member_method();
    }
private:
    member_type* member; // member is now of type T (i.e. member_type)
};

然后你可以这样测试:

TEST(ParentTest, callsMemberMethodOnceAgain)
{
    auto member = MockMember{};
    EXPECT_CALL(member, member_method()).Times(1);

    auto parent = Parent<MockMember>{&member};
    parent.parent_method();
}

修改Parent的界面以提供指定哪个Member传递(或“注入”测试模拟)的方法可能是一个好主意,因为它增加了模块性(即减少了两个对象之间的依赖性)。它使您能够为Parent提供不同的Members,提供不同的功能。如果你有更多的班级成员,这让我对你有几个模板参数的关注。

你不应该嘲笑所有成员,甚至不需要“很多”成员。具有一个责任的大多数“好”大小类具有很少的依赖性。您通常只需要模拟其他具有正交职责且您的课程依赖的类。一个好的设计试图最小化这些依赖性。如果您发现有许多类对其他类有许多依赖关系,则可能表明您的设计可以得到改进。

这使我们有了性能,因为通过多态/继承提供这种模块化是由于额外的间接级别而付出代价。对于模板不是这种情况,因为在编译类型中选择了类型。但是,您可能不希望使用依赖项模板创建所有类,因此您必须达到平衡。很难“猜测”什么会对整个系统的性能产生重大影响,以及产生多大的影响。事实上,除了测量之外,没有可靠的方法。因此,我建议您采取最好的猜测,但要专注于良好的设计,并使您的代码可读并且正常工作。然后,在测量之后,如果存在性能问题,请修复它们。很大程度上取决于您的域名,但根据我的经验,这种间接性很少是性能问题的原因。

注意:我测试了使用gcc版本7.2.0(带有C ++ 17标志)和googletest 1.8.0提供的代码。

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