原文

以下文章基于AI翻译,不准确处请参考原文。

我们到底在说什么?

CMock是一个很好的小工具,它可以获取你的头文件并为它创建一个模拟接口,这样你就可以更容易地对模块进行单元测试。对于头文件中的每个函数原型,如下所示:

int DoesSomething(int a, int b);

…你得到一个自动生成的DoesSomething函数,你可以链接到它,而不是你真正的DoesSomething函数。通过使用这个模拟版本,您可以验证它是否接收到您想要的数据,并让它返回您想要的任何数据,让它在您想要的时候抛出错误,等等……为您最新的实际模块接触的所有东西创建这些,您就会突然处于一个强大的位置:您可以控制和验证您最新创建的每个细节。

为了使这更容易,CMock也给你一堆像下面的函数,所以你可以告诉生成的DoesSomething函数如何为每个测试行为:

void DoesSomething_ExpectAndReturn(int a, int b, int toReturn);

void DoesSomething_ExpectAndThrow(int a, int b, EXCEPTION_T error);

void DoesSomething_StubWithCallback(CMOCK_DoesSomething_CALLBACK YourCallback);

void DoesSomething_IgnoreAndReturn(int toReturn);

你可以把这些东西背靠背堆起来,它就会记住你想传递的内容,比如:

test_CallsDoesSomething_ShouldDoJustThat(void)

{

DoesSomething_ExpectAndReturn(1,2,3);

DoesSomething_ExpectAndReturn(4,5,6);

DoesSomething_ExpectAndThrow(7,8, STATUS_ERROR_OOPS);

CallsDoesSomething( );

}

这个测试将调用CallsDoesSomething,这是我们正在测试的函数。我们期望这个函数调用三次做某事。第一次,我们检查以确保它被称为DoesSomething(1,2),并神奇地返回一个3。第二次我们检查DoesSomething(4,5)并返回一个6。第三次我们验证做了一些事情(7,8),我们将抛出一个错误而不是返回任何东西。如果calls做的事情有任何错误,它将失败测试。如果你没有调用足够的,或调用太多,或使用错误的参数,或以错误的顺序调用,它将会失败。

CMock基于Unity,用于所有内部测试。它使用Ruby完成所有主要工作(版本2.0.0及以上)。

安装

要安装CMock,您需要做的第一件事是获取一个Ruby副本。如果您使用的是linux或osx,那么您可能已经拥有它了。你可以输入以下文字来证明:

ruby --version

如果它以一种暗示无知的方式回应,那么你就需要安装它。你可以去ruby-lang获取最新版本。如果它返回的版本大于2.0.0,您还需要这样做。去做吧。我们会等待。

一旦你有了Ruby,你有三个选择:

Install Ceedling (which has it built in!) through your commandline using gem install ceedling.

生成的Mock模块摘要

除了模拟本身之外,CMock还将生成以下函数,以便在测试中使用。expect函数总是生成的。其他函数只有在这些插件被启用时才会生成:

Expect:

你的基本订书钉将会在你的日常工作中使用。通过调用这个函数,您就告诉CMock您希望在测试期间调用这个函数。它还指定了您希望使用哪些参数调用它,以及在调用时您希望返回什么返回值。为了让多个调用排队,你可以连续多次调用这个函数。

void func(void) => void func_Expect(void)

void func(params) => void func_Expect(expected_params)

retval func(void) => void func_ExpectAndReturn(retval_to_return)

retval func(params) => void func_ExpectAndReturn(expected_params, retval_to_return)

ExpectAnyArgs:

这与expect调用的行为类似,只是它并不真正关心调用mock的参数是什么。它仍然计算模拟被调用的次数,如果有返回值,它仍然处理返回值。注意,没有参数的函数不会生成ExpectAnyArgs调用,因为它的行为与现有的Expect和ExpectAndReturn调用完全相同。

void func(params) => void func_ExpectAnyArgs(void)

retval func(params) => void func_ExpectAnyArgsAndReturn(retval_to_return)

Array:

ExpectWithArray是Expect的另一种变体。与expect一样,它也关心调用模拟的次数、调用模拟时使用的参数和返回的值。不过,这种变体还有另一个特点。对于任何类似于指针或数组的东西,它将实参分解为两个实参。第一个是原始指针。第二个参数指定用于验证该数组的元素数量。如果指定1,它将检查一个对象。如果是2,它会假设你的指针指向数组中两个元素的第一个。如果指定了0个元素,如果配置了:smart模式,它只会检查指针,如果设置了:compare_data则会失败。

void func(void) => (nothing. In fact, an additional function is only generated if the params list contains pointers)

void func(ptr * param, other) => void func_ExpectWithArray(ptr* param, int param_depth, other)

retval func(void) => (nothing. In fact, an additional function is only generated if the params list contains pointers)

retval func(other, ptr* param) => void func_ExpectWithArrayAndReturn(other, ptr* param, int param_depth, retval_to_return)

Ignore:

也许你不关心特定函数被调用的次数或者它被调用的实际参数。在这种情况下,您需要使用Ignore。每个测试只需要调用一次Ignore。然后,它将忽略对该特定mock的任何进一步调用。IgnoreAndReturn的工作方式与此类似,除了它有一个额外的好处,即它知道在调用发生时返回什么。如果mock的调用次数超过IgnoreAndReturn的调用次数,则它将继续返回最后一个值而不会报错。如果它被调用的次数较少,它也会忽略它。你说过你不在乎叫了多少次,对吧?

void func(void) => void func_Ignore(void)

void func(params) => void func_Ignore(void)

retval func(void) => void func_IgnoreAndReturn(retval_to_return)

retval func(params) => void func_IgnoreAndReturn(retval_to_return)

StopIgnore:

也许你想在测试中忽略一个特定的函数,但之后又不想忽略它。在这种情况下,你需要使用StopIgnore,它将取消之前调用的Ignore或IgnoreAndReturn,该函数要求你期待或处理对函数的调用。

void func(void) => void func_StopIgnore(void)

void func(params) => void func_StopIgnore(void)

retval func(void) => void func_StopIgnore(void)

retval func(params) => void func_StopIgnore(void)

IgnoreStateless:

这个插件类似于Ignore插件,但是IgnoreAndReturn函数是无状态的。因此,被忽略的函数总是返回最后一个指定的返回值,而不会像默认插件的IgnoreAndReturn那样将返回值排队。

为了停止忽略一个函数,你可以调用StopIgnore,或者简单地覆盖Ignore。IgnoreAndReturn)与一个期望(resp。ExpectAndReturn)。注意,调用Ignore (resp IgnoreAndReturn)将清除你之前调用的Expect (resp。ExpectAndReturn),因此它们不会在StopIgnore被调用后恢复。

你可以在你的CMock配置文件中使用:ignore_stateless而不是:ignore来使用这个插件。

生成的函数与上面的Ignore和StopIgnore相同。

Ignore Arg:

也许您总体上想使用Expect及其类似的变体,但您并不关心传递给特定参数的是什么。当实参是指向函数应该填充的值的指针时,这一点特别有用。您不希望使用ExpectAnyArgs,因为您仍然关心其他参数。相反,在进行Expect调用之后,您可以调用这个函数。它告诉CMock在测试的其余部分中忽略这个mock函数的一个特定参数。如果需要,您可以调用它的多个实例来忽略每个期望之后的多个参数。

void func(params) => void func_IgnoreArg_paramName(void)

ReturnThruPtr:

另一个对函数的特定参数进行操作的选项是ReturnThruPtr插件。对于每个类似于指针或引用的参数,CMock都会生成该函数的实例。正如AndReturn函数支持将一个或多个返回值注入到队列中一样,这个函数允许指定一个或多个返回值,这些返回值在每次调用mock时被排队并复制到所指向的空间中。

void func(param1) => void func_ReturnThruPtr_paramName(val_to_return)

=> void func_ReturnArrayThruPtr_paramName(cal_to_return, len)

=> void func_ReturnMemThruPtr_paramName(val_to_return, size)

Callback:

如果所有这些选项都不起作用,而您确实需要自定义一些内容,那么您仍然有一个选择。只要您在测试中stub回调函数,它就会在遇到mock时调用回调函数,并返回从回调函数返回的retval(如果有的话)。

void func(void) => void func_[AddCallback,Stub](CMOCK_func_CALLBACK callback) where CMOCK_func_CALLBACK looks like: void func(int NumCalls)

void func(params) => void func_[AddCallback,Stub](CMOCK_func_CALLBACK callback) where CMOCK_func_CALLBACK looks like: void func(params, int NumCalls)

retval func(void) => void func_[AddCallback,Stub](CMOCK_func_CALLBACK callback) where CMOCK_func_CALLBACK looks like: retval func(int NumCalls)

retval func(params) => void func_[AddCallback,Stub](CMOCK_func_CALLBACK callback) where CMOCK_func_CALLBACK looks like: retval func(params, int NumCalls)

您可以从两个选项中选择:

func_AddCallback tells the mock to check its arguments and calling order (based on any Expects you've set up) before calling the callback.

func_Stub tells the mock to skip all the normal checks and jump directly to the callback instead. In this case, you are replacing the normal mock calls with your own custom stub function.

还有一个更老的名字,func_StubWithCallback,它只是func_AddCallback或func_Stub的别名,这取决于:callback_after_arg_check切换项的设置。这是不赞成的,我们建议使用上面的两个选项。

Cexception:

最后,如果使用Cexception进行错误处理,则可以使用它从模拟内部抛出错误。与expect类似,它记得应该是哪个调用抛出错误,而且它仍然首先检查参数。

void func(void) => void func_ExpectAndThrow(value_to_throw)

void func(params) => void func_ExpectAndThrow(expected_params, value_to_throw)

retval func(void) => void func_ExpectAndThrow(value_to_throw)

retval func(params) => void func_ExpectAndThrow(expected_params, value_to_throw)

Running CMock

CMock是一个Ruby脚本和类。因此,您可以直接从命令行使用它,或者将它包含在您自己的脚本或rakefile中。

Mocking from the Command Line

打开CMock后,您将找到CMock。在'lib'目录下。这是您想要运行的文件。它需要一个要模拟的头文件列表,以及一个可选的yaml文件,以获得更详细的配置(请参阅下面的配置选项)。

例如,这将使用MyConfig.yml中指定的配置创建三个模拟:

ruby cmock.rb -oMyConfig.yml super.h duper.h awesome.h

这将使用默认配置创建两个模拟:

ruby cmock.rb ../mocking/stuff/is/fun.h ../try/it/yourself.h

Mocking From Scripts or Rake

CMock可以直接从你自己的脚本或rakefile中使用。首先包含cmock。然后创建一个CMock实例。当你创建你的实例时,你可以用三种方法中的一种来初始化它。

你可以什么都不指定,允许它以默认设置运行:

require 'cmock.rb'

cmock = CMock.new

你可以指定一个YAML文件包含你想要的配置选项:

cmock = CMock.new('../MyConfig.yml')

您可以明确指定选项:

cmock = Cmock.new(:plugins => [:cexception, :ignore], :mock_path => 'my/mocks/')

Creating Skeletons:

CMock不仅能够从头文件生成模拟文件,而且还能够从头文件生成(并更新)框架C文件。它通过为头文件中声明的每个函数创建一个(大部分)空实现来实现这一点。如果您稍后添加了头列表,只需再次运行该特性,它就会为丢失的函数添加原型!

与CMock的普通用例一样,该特性可以从命令行或其ruby API中使用。例如,从命令行添加--skeleton来生成一个骨架:

ruby cmock.rb --skeleton ../create/c/for/this.h

Config Options:

以下配置选项可以在yaml文件中指定,也可以在实例化时直接指定。

作为Ruby传递,它们看起来像这样:

{ :attributes => [“__funky”, “__intrinsic”], :when_ptr => :compare }

在yaml文件中定义,它们看起来更像这样:

:cmock:

:attributes:

- __funky

- __intrinsic

:when_ptr: :compare

在所有情况下,您都可以从默认值中包含您想要覆盖的内容。我们尝试在下面指定默认值。

:attributes:为了测试的目的,CMock应该忽略这些属性。在这里放置自定义编译器扩展和扩展是很方便的。如果你的编译器被一些扩展语法卡住了,这通常是一个很好的地方。

defaults: ['ramfunc', 'irq', '__fiq', 'register', 'extern']

note: 该选项将把这些属性重新插入mock调用。如果这不是你想要的,看看::strippables

:c_calling_conventions: 类似地,CMock可能需要了解在您的代码库中可能出现哪些C调用约定。如果它遇到自己不认识的东西,它不会去嘲笑它。我们提供了最常见的编译器,但也有许多编译器,因此也有许多其他选项。

defaults: ['stdcall', 'cdecl', '__fastcall']

note: 该选项将把这些属性重新插入mock调用。如果这不是你想要的,看看::strippables

:callback_after_arg_check: 告诉 :callback 插件在调用回调函数之前通过将this设置为true来进行常规的参数检查。当为false时,调用回调函数而不是实参验证。

default: false

:callback_include_count:告诉:callbackplugin包含一个额外的参数来指定回调函数被调用的次数。如果设置为false,则回调函数具有与被模拟函数相同的接口。当您想要使用callback作为存根时,这很方便。

default: true

...

Compiled Options:

还有一些#defines用于定制cmock体验。请随意将这些信息传递给编译器或其他最方便的方法。CMock会根据其他设置,特别是Unity的设置,尽力猜测你想要什么。

CMOCK_MEM_STATIC或CMOCK_MEM_DYNAMIC定义其中之一,以确定是否希望在测试期间根据堆的需要动态添加内存。如果是静态的,则可以控制Cmock的总占用空间。如果是动态的,则需要确保为Cmock提供一些堆空间。

CMOCK_MEM_SIZE这是您分配给Cmock的内存总量。在动态模式中,这是一次分配的每个块的大小(更大的数字占用更多内存,但需要更少的mallocs)。

CMOCK_MEM_ALIGN对齐数据的方式。大多数嵌入式设计师都知道,并不是所有的东西都像PC一样灵活。默认值为2,表示对齐到最近的2^2 ->4字节(32位)。你可以通过设置0来关闭对齐,强制对齐到最近的uint16与1,甚至到最近的uint64与3。

CMOCK_MEM_PTR_AS_INT用于内部保存指针…它需要足够大。在大多数处理器上,指针与unsigned long相同…但也许对你来说不是这样?

CMOCK_MEM_INDEX_TYPE这需要一个足够大的东西指向Cmock的内存空间的任何地方…通常是size_t。

Other Tips

resetTest

虽然这并不是严格意义上的CMock特性,但CMock的用户通常要么在Unity中使用测试运行器生成器脚本,要么使用Ceedling。在任何一种情况下,都有一个名为resetest的方便函数,它由您的运行程序生成。然后,您可以在测试本身中使用这个方便的函数。在测试期间调用它,让CMock验证到此为止的所有内容,然后重新开始。当想用不同参数的迭代方式测试一个函数时,这是非常有用的。

C++ Support

c++单元测试/ mock框架通常使用一个完全不同的方法(与CMock)依赖于重载虚拟类成员,不支持直接模拟静态类成员方法或免费功能(例如,函数在纯C)。一种方法是将非虚拟函数封装在一个对象,使它们作为虚拟方法和修改你的代码在运行时注入模拟……但是还有另一种方法!

只需使用CMock来模拟静态成员方法,并使用c++ mock框架来处理虚方法。(是的,你可以在同一个测试中混合CMock和c++ mock框架的模拟!)

请记住,由于c++ mock框架也经常将真实对象链接到单元测试中,我们需要在CMock所模拟的任何函数的真实实现源中解决如下的多个定义错误:

#if defined(TEST)

__attribute__((weak))

#endif

为了解决在不同的命名空间/类中重用相同函数名的潜在问题,生成的函数名包括命名空间和类。例如:

namespace MyNamespace {

class MyClass {

static int DoesSomething(int a, int b);

};

}

会生成如下函数

void MyNamespace_MyClass_DoesSomething_ExpectAndReturn(int a, int b, int toReturn);

例子

如何用C语言测试,C语言单元测试CMock使用(一) 基本用法相关推荐

  1. c语言系统测试工具,c语言测试工具|雨田单元测试系统下载v2.2.2 官方版 - 欧普软件下载...

    雨田单元测试系统是一款适用于C语言文件的单元测试和集成测试系统,雨田单元测试系统可以进行进行黑盒和白盒测试,适合程序猿们测试单元使用. 功能介绍 雨田单元测试系统V1.0可以针对c语言程序文件进行单元 ...

  2. 计算机多媒体技术英语怎么说,计算机多媒体技术环境中的英语语言测试

    计算机多媒体技术环境中的英语语言测试 摘 要:教学的重要环节语言测试,借助多媒体的发展而完善其语言测试的功能.本文阐述了计算机多媒体技术在当前及未来的语言测试中的不可估量的运用价值. 关键词:多媒体; ...

  3. 机器学习驱动的语言测试

    机器学习驱动的语言测试 背景 Item Response Theory (IRT, 项目反应理论或潜在特质理论) Computer-adaptive testing (CAT,计算机自适应测试) Th ...

  4. Go 语言测试在开发中的最佳实践 | 使用 Docker 容器进行测试

    前言 最近看到很多 Go 语言测试的教程都非常水,只讲了测试最基本的用法,几乎没有涉及到在开发中如何去设计一个很出色的测试.这篇博客将会带领大家一步一步完成一个出色的 Go-Test 思考 Go 语言 ...

  5. Go语言——测试与性能

    篇幅较长,建议先收藏 文章目录 测试与性能 1. 测试 1.1 单元测试 1. 第一个测试函数 2. 表组测试 3. 模拟测试 △ 1. HTTP mock 2. 数据库 mock mysql:go- ...

  6. 视频教程-桫哥-GOlang-08程序测试-Go语言

    桫哥-GOlang-08程序测试 多年互联网从业经验: 有丰富的的企业网站.手游.APP开发经验: 曾担任上海益盟软件技术股份有限公司项目经理及产品经理: 参与项目有益盟私募工厂.睿妙影音家庭物联网设 ...

  7. C语言数据结构学习用单元测试

    药物名称: C语言数据结构学习用单元测试 主治: 本品是基于CUnit编写的.全部的.纯C的数据结构(ADT)各种实现的单元测试.主治基础不牢,水品增长缓慢.建议配合严蔚敏<数据结构>(C ...

  8. python rest api 测试_如何用Python编写REST API的单元测试

    在过去的几个月中,正在从事一个名为B的项目.它是带有简单Web UI的徽章生成器,用于添加数据并生成PDF可打印徽章.B后端现在已转移到REST-API并测试REST-API中使用的功能,我们需要一些 ...

  9. c语言测试清单,c语言测试(C language test).doc

    c语言测试(C language test) c语言测试(C language test) * * college course exam papers Course Name: "C: t ...

最新文章

  1. 免费开放!清华、智源、协和联合开发乳腺癌AI诊断工具,可预测分子亚型,准确率高达76%...
  2. laravel 验证器怎么验证json对象_Postman使用tv4进行JSON Schema结构验证和断言
  3. BorderContainer的圆角问题
  4. Java类加载器(二)——自定义类加载器
  5. react前端显示图片_如何在react项目中引用图片?
  6. 监控Squid运行脚本
  7. MongoDB安装和批量写入
  8. BASIC-9 特殊回文数 C语言版
  9. ps考证是人改分的还是电脑
  10. 开题报告、论文摘要、摘要翻译、论文结论怎么写?
  11. 服务器端编程心得(二)—— Reactor模式
  12. 二十岁的男人(应该需要做什么)
  13. 英文日期格式及缩写总结
  14. 实例【GPA计算器】的制作过程之MFC初体验收获
  15. python计算化学浓度_python 计算化学
  16. win快捷键_Windows 被冷落的 WIN 键,其实比你想的更好用
  17. ole db提供程序 mysql_SQL SERVER 链接服务器使用
  18. 台式计算机用手机流量上网,手机流量怎么给台式电脑用
  19. 交换机连接方式1:双机直连
  20. 特征筛选———贷款用户是否会逾期

热门文章

  1. 【速学速记】Python 高阶函数
  2. 特斯拉亚洲最大超级充电站正式运营,可同时提供20辆车的快充服务
  3. redis配置master-slave模式
  4. 新发布AlbumOnNet 、dotnetCharting控件注册资料
  5. Shell每行前面加上行号
  6. 2018-2019-1 20189205 《Linux 内核原理与分析》第三周作业
  7. java webservice wsimport 无法将名称 'soapenc:Array' 解析为 'type definition' 组件 时对应的解决方法...
  8. 【面试】iOS 开发面试题(二)
  9. NSString的比较专栏
  10. javascript-抽象工厂模式