在设置阶段忽略模拟调用

时间:2014-03-30 14:46:41

标签: c++ unit-testing googletest googlemock

我经常遇到这样一个问题,即模拟对象需要在某个状态之前被带到某个状态。测试的一部分可以开始。

例如,我想说我想测试以下类:

struct ToTest
{
    virtual void onEnable();
    virtual void doAction();
};

因此,我创建了以下模拟类:

struct Mock : ToTest
{
    MOCK_METHOD0(onEnable, void());
    MOCK_METHOD0(doAction, void());
};

第一个测试是在启用使用onEnable对象的系统时调用ToTest

TEST(SomeTest, OnEnable)
{
    Mock mock;
    // register mock somehow

    // interesting part of the test
    EXPECT_CALL(mock, onEnable());
    EnableSystem();
}

到目前为止,这么好。第二个测试是当系统执行操作并启用时调用doAction。因此,应该在测试的有趣部分开始之前启用系统:

TEST(SomeTest, DoActionWhenEnabled)
{
    Mock mock;
    // register mock somehow

    // initialize system
    EnableSystem();

    // interesting part of the test
    EXPECT_CALL(mock, doAction());
    DoSomeAction();

}

这有效,但是对onEnable无趣的召唤发出了恼人的警告。这个问题似乎有两个常见的解决方法:

  1. 使用NiceMock<Mock>来抑制所有此类警告;和
  2. 添加EXPECT_CALL(mock, onEnable())声明。
  3. 我不想使用第一种方法,因为可能还有其他无趣的调用确实不应该发生。我也不喜欢第二种方法,因为我已经测试过(在第一次测试中)在启用系统时调用onEnable;因此,我不想在所有适用于已启用系统的测试中重复这种期望。

    我希望能够做的是说所有模拟调用都应该被完全忽略。在这个例子中,我希望只从测试的有趣部分开始检查期望&#34;评价。

    有没有办法使用Google Mock实现这一目标?

3 个答案:

答案 0 :(得分:3)

令人讨厌的是,必要的函数在那里:gmock/gmock-spec-builders.h定义Mock::AllowUninterestingCalls和其他来控制特定模拟对象的警告的生成。使用这些功能,应该可以暂时禁用有关不感兴趣的呼叫的警告。

然而,那个问题是这些功能是私有的。好的是,班级Mock有一些可以被滥用的模板朋友(例如,NiceMock)。所以我创建了以下解决方法:

namespace testing
{
// HACK: NiceMock<> is a friend of Mock so we specialize it here to a type that
// is never used to be able to temporarily make a mock nice. If this feature
// would just be supported, we wouldn't need this hack...
template<>
struct NiceMock<void>
{
    static void allow(const void* mock)
    {
        Mock::AllowUninterestingCalls(mock);
    }

    static void warn(const void* mock)
    {
        Mock::WarnUninterestingCalls(mock);
    }

    static void fail(const void* mock)
    {
        Mock::FailUninterestingCalls(mock);
    }
};

typedef NiceMock<void> UninterestingCalls;
}

这允许我通过UninterestingCalls typedef。

访问私有函数

答案 1 :(得分:0)

根据设计,gmock无法实现您所寻求的灵活性。来自gmock Cookbook(强调我的):

  

[...]你应该非常谨慎地使用唠叨或严格的嘲讽,因为它们往往会使测试变得更脆弱,更难维护。当您重构代码而不改变其外部可见行为时,理想情况下您不需要更新任何测试。但是,如果您的代码与愚蠢的模拟进行交互,那么您可能会因为更改而开始收到警告。更糟糕的是,如果您的代码与严格模拟交互,您的测试可能会开始失败,您将被迫修复它们。 我们的一般建议是在大多数情况下使用漂亮的模拟(不是默认模式),在开发或调试测试时使用naggy mocks(当前默认模式),并且仅使用严格的模拟作为最后的手段。

不幸的是,这是我们和许多其他开发人员遇到的问题。 Jeff Langr在其着作Modern C++ Programming with Test-Driven Development中写道(第5章,关于测试双打):

  

测试设计怎么样?当我们从手动模拟解决方案改为使用Google Mock时,我们将一个测试分成两个。如果我们在单个测试中表达了所有内容,那么一个测试可以设定涵盖所有三个重要事件的期望。这是一个简单的解决方案,但我们最终会得到一个混乱的测试。

     

[...]

     

使用NiceMock,我们承担的风险很小。如果代码稍后以某种方式更改为在[...]接口上调用另一个方法,我们的测试就不会知道它了。您应该在需要时使用NiceMock,而不是习惯性的。如果您经常需要它,请寻求修复您的设计。

答案 2 :(得分:-1)

您可能最好在第二次测试中使用不同的模拟类。

class MockOnAction : public ToTest {
    // This is a non-mocked function that does nothing
    virtual void onEnable() {}   

    // Mocked function
    MOCK_METHOD0(doAction, void());
}

为了使此测试起作用,您可以让onEnable不执行任何操作(如上所示)。或者它可以做一些特殊的事情,比如调用基类或做其他逻辑。

virtual void onEnable() {
    // You could call the base class version of this function
    ToTest::onEnable();

    // or hardcode some other logic
    // isEnabled = true;
}