如何检查成员方法是否被调用
How to check if member method is called
我有一个class(比方说:父)创建 在 Member 对象中。
我想知道,如何测试 member 方法在 parent 方法调用时被调用。
让我们看一个例子:
class Parent : public IParent
{
public:
Member() : member{} {}
void parent_method() override
{
member.member_method();
}
private:
Member member;
}
我想准备测试以检查 member_method 是否在 [= 上被调用27=]parent_method 使用 GTest / GMock 调用。
重要:我想避免多态调用或任何动态分配 - 如果可能的话。
我想在构造函数中传递一些工厂并构建适当的成员对象(真实的或模拟的),但它需要多态调用和动态分配。
我想避免这种开销。
有什么方法可以非常干净地做到这一点吗?
欢迎提出任何建议。提前致谢。
一种解决方案是使用模板。以下是 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();
}
您可以通过修改 Parent::Parent()
来消除 friend
语句,这样您就可以将指针传递给 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
(或传递给 "inject" 测试 Mock)可能是个好主意,因为它增加了模块化(即减少两个对象之间的依赖关系)。它使您能够为 Parent
提供不同的 Member
,从而提供不同的功能。如果您有更多 class 成员,这让我担心您有多个模板参数。
您不需要模拟所有成员,甚至 "many" 成员。大多数 "good" 大小 class 具有一项责任的实体几乎没有依赖关系。您通常只需要模拟其他 class 具有正交职责并且您的 class 依赖的实体。一个好的设计会尽量减少这些依赖性。如果您发现您有许多 class 与其他 class 有很多依赖关系,则可能表明您的设计可以改进。
这给我们留下了性能,因为通过 polymorphism/inheritance 提供这种模块化是有代价的,因为额外的间接级别。对于模板,情况并非如此,因为类型是在编译类型时选择的。但是,您可能不想让所有 classes 都具有依赖项模板,因此您必须达到一个平衡。很难 "guess" 什么会显着影响整个系统的性能以及影响程度。事实上,除了测量之外,没有可靠的方法。因此,我建议您做出最好的猜测,但要专注于良好的设计以及您的代码可读性和正确工作。然后,在测量之后,如果有性能问题,就修复它们。很大程度上取决于您的域,但根据我的经验,这种间接寻址很少会导致性能问题。
注意:我测试了使用 gcc 7.2.0 版(带有 C++17 标志)和 googletest 1.8.0.
提供的代码
我有一个class(比方说:父)创建 在 Member 对象中。
我想知道,如何测试 member 方法在 parent 方法调用时被调用。
让我们看一个例子:
class Parent : public IParent
{
public:
Member() : member{} {}
void parent_method() override
{
member.member_method();
}
private:
Member member;
}
我想准备测试以检查 member_method 是否在 [= 上被调用27=]parent_method 使用 GTest / GMock 调用。
重要:我想避免多态调用或任何动态分配 - 如果可能的话。
我想在构造函数中传递一些工厂并构建适当的成员对象(真实的或模拟的),但它需要多态调用和动态分配。 我想避免这种开销。
有什么方法可以非常干净地做到这一点吗?
欢迎提出任何建议。提前致谢。
一种解决方案是使用模板。以下是 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();
}
您可以通过修改 Parent::Parent()
来消除 friend
语句,这样您就可以将指针传递给 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
(或传递给 "inject" 测试 Mock)可能是个好主意,因为它增加了模块化(即减少两个对象之间的依赖关系)。它使您能够为 Parent
提供不同的 Member
,从而提供不同的功能。如果您有更多 class 成员,这让我担心您有多个模板参数。
您不需要模拟所有成员,甚至 "many" 成员。大多数 "good" 大小 class 具有一项责任的实体几乎没有依赖关系。您通常只需要模拟其他 class 具有正交职责并且您的 class 依赖的实体。一个好的设计会尽量减少这些依赖性。如果您发现您有许多 class 与其他 class 有很多依赖关系,则可能表明您的设计可以改进。
这给我们留下了性能,因为通过 polymorphism/inheritance 提供这种模块化是有代价的,因为额外的间接级别。对于模板,情况并非如此,因为类型是在编译类型时选择的。但是,您可能不想让所有 classes 都具有依赖项模板,因此您必须达到一个平衡。很难 "guess" 什么会显着影响整个系统的性能以及影响程度。事实上,除了测量之外,没有可靠的方法。因此,我建议您做出最好的猜测,但要专注于良好的设计以及您的代码可读性和正确工作。然后,在测量之后,如果有性能问题,就修复它们。很大程度上取决于您的域,但根据我的经验,这种间接寻址很少会导致性能问题。
注意:我测试了使用 gcc 7.2.0 版(带有 C++17 标志)和 googletest 1.8.0.
提供的代码