在设置阶段忽略模拟调用

Job*_*Job 5 c++ unit-testing googletest googlemock

我经常面临这样一个问题,即模拟对象需要在测试的“有趣”部分开始之前进入某种状态。

例如,假设我想测试以下类:

struct ToTest
{
    virtual void onEnable();
    virtual void doAction();
};
Run Code Online (Sandbox Code Playgroud)

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

struct Mock : ToTest
{
    MOCK_METHOD0(onEnable, void());
    MOCK_METHOD0(doAction, void());
};
Run Code Online (Sandbox Code Playgroud)

第一个测试是onEnableToTest启用使用对象的系统时调用:

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

    // interesting part of the test
    EXPECT_CALL(mock, onEnable());
    EnableSystem();
}
Run Code Online (Sandbox Code Playgroud)

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

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

    // initialize system
    EnableSystem();

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

}
Run Code Online (Sandbox Code Playgroud)

这有效,但会发出令人讨厌的警告,提示对onEnable. 这个问题似乎有两个常见的修复方法:

  1. 使用NiceMock<Mock>能禁止所有此类警告; 和
  2. 添加EXPECT_CALL(mock, onEnable())声明。

我不想使用第一种方法,因为可能还有其他不应该发生的无趣的调用。我也不喜欢第二种方法,因为我已经测试过(在第一个测试中)onEnable系统启用时调用的方法;因此,我不想在所有适用于已启用系统的测试中重复这种期望。

我希望能够做的是说到某个点的所有模拟调用都应该被完全忽略。在此示例中,我希望仅从“测试的有趣部分”注释开始检查期望值。

有没有办法使用 Google Mock 来实现这一点?

Job*_*Job 5

烦人的是,有必要的函数: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;
}
Run Code Online (Sandbox Code Playgroud)

这让我可以通过 typedef 访问私有函数UninterestingCalls


Lil*_*ste 1

根据设计,您所寻求的灵活性在 gmock 中是不可能实现的。来自gmock Cookbook(重点是我的):

\n
\n

[...] 您应该非常谨慎地选择何时使用 naggy 或 strict 模拟,因为它们往往会使测试变得更脆弱且更难以维护。当您重构代码而不更改其外部可见行为时,理想情况下您不需要更新任何测试。但是,如果您的代码与烦人的模拟交互,您可能会因为更改而开始收到垃圾邮件警告。更糟糕的是,如果您的代码与严格的模拟交互,您的测试可能会开始失败,并且您将被迫修复它们。我们的一般建议是在大多数情况下使用良好的模拟(尚未默认),在开发或调试测试时使用 naggy 模拟(当前默认),并且仅将严格模拟作为最后的手段。

\n
\n

不幸的是,这是我们和许多其他开发人员都遇到过的问题。Jeff Langr 在他的《现代 C++ 编程与测试驱动开发》一书中写道(第 5 章,关于测试替身):

\n
\n

测试设计又如何呢?当我们从手动模拟解决方案更改为使用 Google Mock 的测试时,我们将一项测试一分为二。如果我们在一次测试中表达所有内容,那么一次测试就可以建立涵盖所有三个重要事件的期望。这\xe2\x80\x99s 是一个简单的修复,但我们\xe2\x80\x99d 最终得到了一个混乱的测试。

\n

[...]

\n

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

\n
\n