如何在 C 中模拟同一 UUT 中的函数

Posted

技术标签:

【中文标题】如何在 C 中模拟同一 UUT 中的函数【英文标题】:How to mock function in same UUT in C 【发布时间】:2016-11-08 11:32:29 【问题描述】:

我正在学习在现有的嵌入式 C 代码库(我可以调整以适应)上使用 Ceedling 和 CMock 进行模拟的单元测试。

我遇到过这样的情况,一个单元中的一个函数调用同一单元中的第二个函数。

int foo_a(int r) 

    /* foo_a work */
    /* do not wish to test this function just to test foo_b. */


int foo_b(int i) /* function to test */

    /* foo_b work */
    if (some_condition)
        foo_a(k); /* need to test if foo_a() is called or not. */

我不想将foo_a() 作为foo_b() 测试的一部分进行测试。如何模拟 foo_a() 以便我仍然可以测试它是否被调用但不测试 foo_a() 函数本身?

【问题讨论】:

我对 C 了解不多,但如果它与许多其他语言一样,可以将方法标记为虚拟/可覆盖。在这种情况下,您可以提供一个已知/设置返回为foo_a 的模拟/伪造。这样,在使用模拟/假类时,当 you.call foo_b 时,您将使用 foo_a 的模拟/覆盖方法而不是真正的 impl,因此您可以专注于测试 foo_b 而不必担心 @987654330 @impl。 @kritner C 不是面向对象的。它没有虚拟、可覆盖、继承或多态等概念。 【参考方案1】:

如果不修改源代码,您将无法做到这一点。

考虑将函数拆分为两个独立的编译单元。

或者,在要替换的函数周围添加#ifndef TEST

【讨论】:

这让我可以引入一个假货,但我如何确定假货是否被调用?首先的想法是简单地引入一个全局标志,如果调用了假,则设置该标志,然后在我的测试中检查该标志。有没有更好的方法或更推荐的做法?【参考方案2】:

根据我使用 ceedling 的经验,最好的方法是创建自定义 TEST_ASSERT_FOO_A_IS_CALLED(); 或,细微的差别,TEST_ASSERT_IS_DOING_WHAT_FOO_A_SHOULD_BE_DOING(); 在第二种情况下,您正在测试是否正在执行某些操作,而不是您不应该注意的内部功能。

理论上,使用单元测试你不关心该函数如何运行(即你不关心 foo_b() 是否调用 foo_a(),只要 foo_b() 对模拟函数执行与 foo_a() 相同的事情)和您不应该知道是否调用了诸如foo_a() 之类的内部(私有)函数。因此,您应该测试是否正在调用模拟函数,而不是内部函数本身。

如何创建自定义 ASSERT? 很简单,用普通的C宏定义就好了,例子:

#define TEST_ASSERT_FOO_A_IS_CALLED() \
                                     \
  TEST_ASSERT_CALLED(some_mocked_function_foo_a_is_expected_to_call);                              \
  TEST_ASSERT_EQUAL_MESSAGE(expected_value, some_mocked_function_foo_a_is_expected_to_call_fake.arg0_val, "Something wrong with arg0!"); \

包含“_MESSAGE”断言不是必需的,但更容易发现宏中的哪个断言失败,因为宏中的每个“失败”都会显示相同的行号,因此更难发现。

如果由于某种原因经常发生这种情况,则表明foo_a() 应该在另一个可以模拟的源文件中。

【讨论】:

以上是关于如何在 C 中模拟同一 UUT 中的函数的主要内容,如果未能解决你的问题,请参考以下文章

如何维护 C++/STL 中的函数列表?

如何在函数中模拟函数。是不是可以使用两个或多个补丁来模拟函数中的函数调用?

如何在 VHDL 中使用“函数”从同一计算中返回多个变量?

笑话:当同一个模块也有命名导出时,如何模拟默认导出组件?

如何在win32编程中,在同一个窗口下播放多个音乐

C : 你如何模拟“异常”?