【问题标题】:HippoMocks - Use of "ExpectCallFunc" after "NeverCallFunc" for the same function leads to unexpected "HippoMocks::ExpectationException"HippoMocks - 在“NeverCallFunc”之后对同一函数使用“ExpectCallFunc”会导致意外的“HippoMocks::ExpectationException”
【发布时间】:2016-06-25 23:17:02
【问题描述】:

假设我们有一个单元测试,它首先执行我们期望函数someFunc 不被调用的代码序列,然后执行我们期望函数只被调用一次的代码序列。使用HippoMocks,我们可以这样写:

#include <hippomocks.h>

void someFunc (void)
{

}

int main (int argc, char ** argv)
{
    MockRepository mocks;
    mocks.autoExpect = false;

    mocks.NeverCallFunc(someFunc); // line 27

    /* some testing code ... */

    /* ... in the course of which someFunc does not get called ... */

    mocks.ExpectCallFunc(someFunc); // line 33

    /* other testing code ... */
    someFunc();
    /* ... in the course of which someFunc got called */

    return 0;
}

但是,在 Windows 上运行上述 sn-p(使用 Cygwin 工具链编译)时,会抛出 HippoMocks::ExpectationException

terminate called after throwing an instance of 'HippoMocks::ExpectationException'
what():  Function someFunc() called with mismatching expectation!
Expectations set:
../main.cpp(33) Expectation for someFunc() on the mock at 0x0 was not satisfied.
Functions explicitly expected to not be called:
../main.cpp(27) Result set for someFunc() on the mock at 0x0 was used.

所以我想知道...

... (1),如果 HippoMocks 不是为处理这种情况而设计的。是否期望 someFunc 被调用(第 33 行)不会取代相应模拟存储库中的先前期望?

... (2),为什么不满足第二个期望(第 33 行),因为 someFunc 被明确地调用。如果有的话,我会期望第一个期望(第 27 行)没有得到满足?

有趣的是,事情正好相反。以下 sn-p 运行没有任何问题:

#include <hippomocks.h>

void someFunc (void)
{

}

int main (int argc, char ** argv)
{
    MockRepository mocks;
    mocks.autoExpect = false;

    mocks.ExpectCallFunc(someFunc); // line 27

    /* some testing code ... */
    someFunc();
    /* ... in the course of which someFunc got called */

    mocks.NeverCallFunc(someFunc); // line 33

    /* other testing code ... */

    /* ... in the course of which someFunc does not get called ... */

    /* someFunc(); */ 

    return 0;
}

此外,如果对 someFunc 的调用被插入到第二个 sn-p 中的第二个期望后面(如评论中所示),则会检测到这并报告为违反 HippoMocks 的“从不调用”期望作为一个期望:

terminate called after throwing an instance of 'HippoMocks::ExpectationException'
  what():  Function someFunc() called with mismatching expectation!
Expectations set:
../main.cpp(27) Expectation for someFunc() on the mock at 0x0 was satisfied.
Functions explicitly expected to not be called:
../main.cpp(33) Result set for someFunc() on the mock at 0x0 was used.

HippoMocks 专家的任何帮助将不胜感激...

【问题讨论】:

    标签: c++ unit-testing hippomocks


    【解决方案1】:

    NeverCallExpectCall 之间的交互之前并没有真正考虑过 - 我从没想过人们会经常使用 NeverCall,所以我没有意识到这种交互。

    在当前发布的版本中,NeverCall 会自动预期之前的ExpectCall,并且首先会检查所有NeverCalls。这导致ExpectCall/NeverCall 按预期工作 - 即,您首先获得预期,然后NeverCall 处于活动状态。如果NeverCall/ExpectCall 没有添加autoexpects 并且NeverCall 获得优先级。这是违反直觉的,我认为最好交换行为,以便ExpectCalls 在可以匹配的情况下始终获得优先权。

    我已将您的示例添加为新的测试用例并交换了NeverCallExpectCall 的匹配顺序。我还删除了NeverCalls 的autoExpect - 因为它本来就不应该存在。结果是您的两个示例现在都将以您期望的方式工作,autoExpect 打开或关闭。如果someCallNeverCall 设置之后,您的第二个示例可以工作。这使您可以比以前更多地对设置代码进行分组。

    如果你想测试/切换到这个版本,注意它还在cpp11分支上。当确认分支稳定且文档正确扩展时,我会将其合并到 master 和 release。目前没有已知问题。它在 Github 上:https://github.com/dascandy/hippomocks/tree/cpp11

    【讨论】:

      【解决方案2】:

      您不能这样做,因为 NeverCall 旨在为断言抛出异常。 对我来说似乎完全不合逻辑。

      如果你真的想解决这个问题,请设置以下内容

      mocks.OnCallFunc(someFunc).Do(someFuncCalledHandler);
      

      在您自己的处理程序中,您可以实现所需的逻辑:

      bool callAllowed = true; //global variable
      void someFuncCalledHandler()
      {
          if (!callAllowed)
          {
              throw MyNeverCallException();
          }
      }
      

      在您的测试中,您可以控制 someFuncCalledHandler() 的行为:

      callAllowed = false;
      someFunc();
      

      顺便说一句:在测试中混合排列和操作代码是个坏主意,就像您在示例代码中所做的那样

      【讨论】:

      • 我同意测试代码和测试代码不应该混淆——这只是为了有一个紧凑的问题。
      • 但是,我不同意 NeverCallExpectCall 在交互中的使用没有意义:除了在测试代码中强调在执行期间不得调用给定函数的方面的特定代码部分 - 虽然期望在另一个部分的执行期间被调用 - 通过NeverCall 明确说明这一点也会在不满足所需约束的情况下产生更清晰的消息 - 特别是在涉及函数参数的情况下。
      • 提供的解决方案能帮助您解决问题吗?
      • 好吧,它解决了紧凑示例的问题。但是想象一下,我们不仅期望someFunc 在特定部分中被调用,而且还希望通过DosomeFunc 的工作委托给另一个函数,这在现实世界的场景中很常见。此外,假设我们在原始用例中拥有多个功能。我认为所有标志和手动永不调用处理程序很快就会使事情变得混乱。所以我仍然希望有一个不需要手动复制NeverCallFunc约束功能的解决方案......
      【解决方案3】:

      在调用MockRepository 的析构函数或显式调用其VerifyAll() 方法时检查Hippomocks 期望。在第 33 行之前调用 VerifyAll(),您的第一个示例应该可以正常工作。

      我赞同上一个答案,即测试代码和被测试代码不应该混淆,但我可以想象这只是为了一个紧凑的问题。

      【讨论】:

      • 虽然它并没有消除每次面对所描述的场景时都需要在测试流中插入额外代码的需要,但手动调用VerifyAll 似乎是一个可以接受的解决方案——至少它可以避免这种需要通过Do 添加自己的转向行为实现... 但是,您建议的VerifyAll 用法对我不起作用(使用latest version of Hippomocks 测试)。仍然得到与最初描述的相同的输出。您使用的是哪个版本的 Hippomocks?
      • 至于混合测试代码和测试代码,你是完全正确的:我的意图是使示例代码尽可能紧凑。
      • 我用我们的 hippomocks.h 版本(如果我没记错的话是版本 4)测试了原始代码和修改后的代码 VerifyAll。两者(!)都可以正常工作。我用你提到的版本测试了两者,都抛出了异常。恐怕我的回答不能回答你的问题,但很抱歉我没有机会进一步分析这些差异。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-07-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-02-27
      相关资源
      最近更新 更多