单元测试 - stub - 桩 - mock - 模拟

单元测试 - stub - 桩 - mock - 模拟

1. 单元测试

单元测试 (unit testing) 是指对软件中的最小可测试单元进行检查和验证。对于单元测试中单元的含义,要根据实际情况去具体含义,C 语言中单元指一个函数,Java 中单元指一个类,图形化软件中可以指一个窗口或一个菜单等。单元就是人为规定的最小的被测功能模块。单元测试是在软件开发过程中要进行的最低级别的测试活动,软件的独立单元将在与程序的其他部分相隔离的情况下进行测试。

与单元测试联系起来的一些开发活动包括代码走读 (code review)、静态分析 (static analysis) 和动态分析 (dynamic analysis)。

  • 静态分析是对软件的源代码进行研读,查找错误或收集一些度量数据,并不需要对代码进行编译和执行。
  • 动态分析是通过观察软件运行时的动作,来提供执行跟踪,时间分析,以及测试覆盖度方面的信息。

单元测试是由程序员自己来完成,程序员有责任编写功能代码,同时也就有责任为自己的代码编写单元测试。执行单元测试,就是为了证明这段代码的行为和我们期望的一致。

单元测试是编码工作的一部分,由程序员完成,经过了单元测试的代码才是已完成的代码,提交产品代码时也要同时提交测试代码。测试部门可以作一定程度的审核。

interposition [ˌɪntəpə'zɪʃən]:n. 插入,干涉,调停,提出异议
stub [stʌb]:n. 树桩,(铅笔,香烟等的) 剩余部分,票根 v. (脚趾) 踢到,连根拔起,捻灭 (香烟,雪茄等)
mock [mɒk]:v. 嘲笑,(模仿) 嘲弄,不尊重,蔑视 adj. 虚假的,不诚实的,模仿的,模拟的 n. (英国) 模拟考试
hook [hʊk]:n. 挂钩,吊钩 vt. 钩住,引上钩 vi. 钩住,弯成钩状

2. stub - 桩

桩代码就是用来代替某些代码的代码。单元测试应避免编写桩代码。

产品函数或测试函数调用了一个未编写的函数,可以编写桩函数来代替该被调用的函数,桩代码也用于实现测试隔离。采用由底向上的方式进行开发,底层的代码先开发并先测试,可以避免编写桩代码。这样做的好处是减少了工作量,测试上层函数时,也是对下层函数的间接测试。当下层函数修改时,通过回归测试可以确认修改是否导致上层函数产生错误。

桩函数 (stub) 是模拟被测试模块所调用的模块。桩或桩代码是指用来代替关联代码或者未实现代码的代码。如果函数 F 用 F1 来代替,F 称为原函数,F1 称为桩函数。打桩就是编写或生成桩代码。

打桩的目的有隔离、补齐、控制:

  • 隔离是指将测试任务从产品项目中分离出来,使之能够独立编译、链接,并独立运行。隔离的基本方法就是打桩,将测试任务之外的,并且与测试任务相关的代码,用桩来代替,从而实现分离测试任务。例如函数 A 调用了函数 B,函数 B 又调用了函数 C 和 D。如果函数 B 用桩来代替,函数 A 就可以完全割断与函数 C 和 D 的关系。
  • 补齐是指用桩来代替未实现的代码。例如,函数 A 调用了函数 B,而函数 B 由其他程序员编写,且未实现。可以用桩来代替函数 B,使函数 A 能够运行并测试。补齐在并行开发中很常用。
  • 控制是指在测试时,人为设定相关代码的行为,使之符合测试需求。

一般来说,桩函数要具有与原函数完全一致的原型,仅仅是实现不同,这样测试代码才能正确链接到桩函数。

  • 用于实现隔离和补齐的桩函数一般比较简单,只需把原函数的声明拷过来,加一个空的实现,能通过编译链接就可以。
  • 比较复杂的是实现控制功能的桩函数,要根据测试的需要,输出合适的数据。

C++Test 是一个功能强大的自动化 C/C++ 单元级测试工具,可以自动测试任何 C/C++ 函数、类,自动生成测试用例、测试驱动函数或桩函数,在自动化的环境下极其容易快速的将单元级的测试覆盖率达到 100%。

  • 接口是实现多重继承的途径,而生成遵循某个接口的对象的典型方式就是工厂方法设计模式。与直接调用构造器不同,我们在工厂对象上调用的是创建方法,而该工厂对象将生成接口的某个实现的对象。理论上,通过这种方式,我们的代码将完全与接口的实现分离,使得我们可以透明地将某个实现替换为另一个实现。
  • 利用 C 编译器的预编译特点,通过宏定义替换需要打桩的函数。
  • 修改函数内存地址,通过 jump 指令跳转到 stub 函数

3. mock - 模拟

mock 测试是在测试过程中,对于某些不容易构造或不容易获取的对象,用一个虚拟的对象来创建以便测试的测试方法。虚拟的对象就是 mock 对象。mock 对象就是真实对象在调试期间的代替品。

使用范畴:
真实对象具有不可确定的行为,产生不可预测的效果 (股票行情,天气预报)。
真实对象很难被创建。
真实对象的某些行为很难被触发。
真实对象实际上还不存在。

在这里插入图片描述

mock 可以用来模拟 stub。
stub 关注交互行为,为了验证被测系统调用目标系统接口的交互行为。
mock 关注状态,为了验证调用目标系统后,目标系统的状态。

4. interposition - 打桩

库打桩 (interposition) 是由 Linux 链接器提供的技术,允许用户截获对共享库函数的调用,并执行自己的代码 (当然是在普通权限下,管理员权限通常是禁止使用该技术的)。使用打桩机制,可以追踪某个特殊库函数的调用次数、验证并追踪其输入输出,甚至把它替换成一个完全不同的实现。

给定需要打桩的目标函数,常见一个 wrapper 函数,其原型和目标函数一致。利用特殊的打桩机制,可以实现让系统调用你的 wrapper 函数而不是目标函数。wrapper 函数中通常会执行自己的逻辑,然后调用目标函数,再将目标函数的返回值传递给调用者。

打桩可以发生在编译时、链接时或者程序被加载执行的运行时。不同的阶段都有对应的打桩机制,也有其局限性。基本目标是用打桩来追踪程序运行时对函数的调用。

编译时打桩需要访问程序的源代码,连接时打桩需要能够访问程序的可重定位的对象文件。运行时打桩仅需要访问可执行目标文件即可,它的基本原理是基于动态链接器的 LD_PRELOAD 环境变量的。如果 LD_PRELOAD 环境变量被设置为一个共享库路径的列表 (以空格或分号分隔),那么当你加载和执行一个程序,需要解析未定义的引用时,动态链接器会先搜索 LD_PRELOAD 中给定的库,然后才搜索任何其他的库。有了这个机制,当你加载和执行任意可执行文件时,可以对任何共享库中任意函数打桩。

References

CUnit - A unit testing framework for C
http://cunit.sourceforge.net/doc/index.html
A lightweight library to simplify and generalize the process of writing unit tests for C applications.
https://github.com/google/cmockery

发布了509 篇原创文章 · 获赞 1824 · 访问量 110万+

猜你喜欢

转载自blog.csdn.net/chengyq116/article/details/104681324