如何在C++中模拟成员对象

How to Mock Member Objects in C++

本文关键字:模拟 成员对象 C++      更新时间:2023-10-16

我想为我们的项目创建一个单元测试环境。但是我不知道如何为班级成员创建模拟。我想用一个例子来解释我的问题。

在我的旧项目中,我们使用了一种模拟选择机制,在我看来这是非常丑陋的。这是老年人的方法:

class member {
};
class member_mock_1 {
};
class member_mock_2 {
};
class parent {
#if defined UNIT_TEST_1
  typedef member_t member_mock_1;
#elif defined UNIT_TEST_2
  typedef member_t member_mock_2;
#else
  typedef member_t member;
#endif
private:
  member_t mem;
};

第一个问题是用父类的typedefing来模拟成员对象的类是否是一种正确的方法?最佳实践是什么?如果我想使用单元测试框架,比如 gtest,我应该使用这种方式还是有另一种方法来嘲笑成员?

注1:如果虚拟机制被激活,可以创建基类来简化模拟,如果一个类是pod什么的,我不想使用这个机制。

注意2:我也觉得将成员类型作为模板参数传递很丑陋,一切都成为项目中的模板。我不想那样做。下面是一个示例:

template <typename M>
class parent {
private:
  M mem;
};
#if defined UNIT_TEST_1
  typedef parent_t parent<member_mock_1>;
#elif defined UNIT_TEST_2
  typedef parent_t parent<member_mock_2>;
#else
  typedef parent_t parent<member>;
#endif

这是我在这里建议的方法:

member_mock_1.hpp

class member_mock_1 {
};

member_mock_2.hpp

class member_mock_2 {
};

模拟.hpp

template <typename TYPE>
struct mock { using type = TYPE; };
#define ENABLE_MOCKING(NamE) 
using NamE ## _t = mock<NamE>::type

member_mock.hpp

#if define UNIT_TEST_1
  template<>
  struct mock<member> { using type = member_mock_1 };
#endif
#if define UNIT_TEST_2
  template<>
  struct mock<member> { using type = member_mock_2 };
#endif

会员.hpp

class member {
};
ENABLE_MOCKING(member);

家长.hpp

class parent {
private:
  member_t mem;
};   

我上面提到的方法适用于普通类。我认为,对于模板类,应该做一些额外的工作。

因此,作为结论,我建议使用像上面这样的单元测试结构。可能没有必要,还有其他一些机制或方法可以满足该要求。也许我还在重新发明轮子:(

请建议一种你知道的嘲笑班级成员的方法。

谢谢。

是的,您正在重新发明轮子 代码看起来非常混乱:

#if defined UNIT_TEST_1
  typedef parent_t parent<member_mock_1>;
#elif defined UNIT_TEST_2
  typedef parent_t parent<member_mock_2>;
#else
  typedef parent_t parent<member>;
#endif

有几种工具可用。我使用Typemock Isolator++,因为您可以模拟几乎所有内容,而无需触及您的生产。还有一件事,您在模拟上设置的行为将仅在测试范围内应用,因此每个测试都有一个单独且独立的设置。

您可以访问该成员,即使它是私有的:

member* mock_member = FAKE<member>;
parent* my_parent = new parent();
ISOLATOR_SET_MEMBER(my_parent, mem, mock_member);

并且很容易得到它:

member* get_member;
ISOLATOR_GET_MEMBER(my_parent, mem, get_member);

此外,它允许伪造抽象类、全局方法、纯虚拟方法、私有和受保护方法,为其设置行为。此外,访问隐藏的数据成员并调用它们。查看此内容以获取更多信息。

我的情况与您类似 - 将单元测试引入遗留C++项目。为此,我使用了很多预处理器指令以及Google Test和Google Mock。特别是,如果我面对你的例子,我会做如下:

#if defined UNIT_TEST
class imember
{
    virtual void a_method() = 0;
};
#endif
class member
#if defined UNIT_TEST
: public imember
#endif
{
    void a_method()
    {
        // do something
    };
};
class parent {
public:
#if defined UNIT_TEST
    parent(imember mem) : mem_(mem) {};
#endif
private:
#if defined UNIT_TEST
    imember mem_;
#else
    member mem_;
#endif
};

现在,使用Google Mock定义一个模拟类:

class mockmember : public imember
{
public:
    MOCK_METHOD0(a_method, void());
};

模拟课现已准备就绪。使用 Google 测试定义您的测试方案:

class parenttest : public testing::Test
{
public:
    parenttest() : member_(mockmember()), parent_(member_) {}
    virtual void SetUp() {}
    virtual void TearDown() {}
protected:
    parent parent_;
    mockmember member_;
};
TEST_F(parenttest, a_func)
{
    EXPECT_CALL(member_, a_method());
    int ret = parent_.a_func();
    ASSERT_EQ(0, ret);
}

免责声明,我在Typemock工作。

山姆是完全正确的。此外,您不需要为每个单元测试创建 3 个不同的 member_mock 类。

您可以简单地设置行为,例如,为 member 中的某些私有方法设置行为:

member* mock_member = FAKE<member>;
PRIVATE_WHEN_CALLED(member, somePrivateMethod()).Return(0);

接下来使用 PRIVATE_WHEN_CALLED(mock_member, somePrivateMethod()) 将重载 somePrivateMethod() 的行为,因此,与其创建大量不同的模拟类,只需根据需要更改行为即可。

希望对您有用!