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

时间:2016-02-26 08:18:34

标签: c++ unit-testing mocking

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

在我的旧项目中,我们使用的模拟选择机制在我看来非常难看。这是老方法:

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 {
};

mock.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

member.hpp

class member {
};

ENABLE_MOCKING(member);

parent.hpp

class parent {
private:
  member_t mem;
};   

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

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

请提供一种嘲笑某个班级成员的方法。

感谢。

3 个答案:

答案 0 :(得分:2)

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

#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);

此外,它允许伪造抽象类,全局方法,纯虚方法,私有和受保护方法,为它设置行为。此外,访问隐藏的数据成员并调用它们。查看this了解详情。

答案 1 :(得分:1)

我遇到了类似的情况 - 将单元测试引入遗留的C ++项目。为此,我使用了很多预处理程序指令以及Google TestGoogle 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 Test定义测试方案:

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);
}

答案 2 :(得分:0)

免责声明,我在Typemock工作。

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

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

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

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

希望它对你有用!