《测试驱动的嵌入式C语言开发》——3.4节写第一个测试

3.4 写第一个测试
现在测试列表已经有了,我们可以开始了。很自然,第一个测试是去测试初始化是否正确。LED在初始化后应当全部关闭。
首先我们要建立LedDriver测试文件。按照惯例,可以将它命名为LedDriverTest.c。我通常把测试代码放在一个与产品代码不同的目录里。我会把这些代码放在unity/LedDriver目录中,并调整makefile从而让它能编译和链接这个新的测试文件。给测试起个合适的名字来反映我们要实现的目标,这个文件看起来如下所示:

现在让测试检查一些有意义的东西。看一下测试列表,驱动程序的一个职责是在初始化时把所有的LED都关闭。
如何进行检查呢?自动化测试没办法查看LED,不是吗?这需要有光感设备或者光电设备才行,不是吗?的确,在软硬件集成时我们要去看LED,但是在单元测试时我们可以虚拟地来看它们。
在目标硬件上有一个特定的地址——一个I/O内存映射地址,真正地连接到电路上。写到这个地址中的比特决定了是打开还是关闭特定的LED。在调用LedDriver_Create()时驱动程序必须把一个16比特的0值写进LED的I/O地址中,以便把所有的LED关闭。
这是一本关于TDD的书,因此另一个设计目标是LedDriver必须独立于硬件可测(也就是说在开发环境中可测)。如果在测试过程中驱动程序要向目标硬件的物理地址中写入数据,这会是个问题:内存崩溃或内存访问失败。让我们来重定位这个地址,看看如何让这段代码在开发环境中可测。
为驱动程序伪造环境
如果这个地址是外界传给驱动程序的,那么在测试用例中就可以用有一系列虚拟LED的地址来伪造成真正的物理地址。所谓虚拟LED无非就是一个与LED内存映射有着同样比特位数的变量。测试用例可以设置、重置以及读取代表这个虚拟LED的变量。驱动程序不会知道它被捉弄了。它只是按部就班地如同操作内存映射设备一样打开RAM中的一个比特。
虚拟LED工作起来没有问题,但我们要检查什么值呢?设计说明和测试列表会指引我们要检查什么值。设置某一个LED位为0会把这个LED关闭,设置为1会把它打开。硬件启动时每个LED都处于打开状态。按照设计说明,由软件负责在初始化时把所有的LED关闭。所以我们最好能保证虚拟LED的每一位都被置为0。
为了测试LED是否被正确初始化,我们要写以下测试:

这里有个很微妙的地方。在TEST(LedDriver, LedsOffAfterCreate)中virtualLeds先被置为0xFFFF,随后调用了LedDriver_Create(&virtualLeds)。把virtualLeds初始化成0xFFFF确保了测试可以把vitualLeds碰巧为0和被正确地初始化为0区分开。
并且,请注意virtualLeds的类型。这里用一个16位无符号整数表示LED,与LED在I/O空间内存映射的宽度匹配。测试和产品代码须要至少在两个机器上运行:开发系统和目标设备。结果是,virtualLeds的宽度必须明确指定。如你所知,int的大小在不同架构的机器上是不一样的。使用可移植的整型,如stdint.h中的uint16_t,能让我们强制把该整型的长度在任何机器上都指定为16位。
如我们期望的一样,编译会出错:

依赖注入
把virtualLeds传给驱动程序的做法称作“依赖注入”(dependency injection)。并不是让驱动程序在编译时知道并依赖于LED的地址,而是我们在运行时将其注入。只有目标系统的初始化函数在编译时依赖LED物理地址。
使用依赖注入的一个有意义的附带好处是LedDriver的可重用性更好了。可以将驱动程序放到库中让别的有着不同LED地址的系统使用。这同时也是一个TDD自然地引导出灵活设计的例子。
不要让编码跑到测试前面
如果你在设想最终的代码,这种不完整的实现可能会让你很困惑。并没有将ledsAddress保存在任何地方,很明显它必须保存在某个地方。至少现在还不要保存它,因为还没有一个失败的测试要求保存它。这需要写出一个测试,除非你把地址保存下来,否则就会失败。下一个测试会迫使驱动程序使用前面传入的ledsAddress。
我知道忍住不去写那些你已经知道会用到的代码是很难的,但还是不要写。让编码跟在测试之后,坚持这样的原则就能产生被全面的测试用例彻底测试过的产品代码。
Bob Martin编写的“TDD三条原则”给我们提供了一个如何在写测试代码和产品代码之间切换的指导。在有测试要求保存地址之前就这样做,这将违反Bob的TDD三条原则。

时间: 2024-09-20 18:21:35

《测试驱动的嵌入式C语言开发》——3.4节写第一个测试的相关文章

《测试驱动的嵌入式C语言开发》——导读

目 录 第1章 测试驱动开发1.1 为什么我们需要TDD1.2 什么是测试驱动开发1.3 TDD的机理1.4 TDD的微循环1.5 TDD的好处1.6 对于嵌入式开发的益处第一部分 开 始第2章 测试驱动开发的工具和约定2.1 什么是自动化单元测试框架2.2 Unity:一个全部用C实现的自动化测试框架2.3 CppUTest:一个用C++实现的自动化单元测试框架2.4 单元测试也会崩溃2.5 "四阶段"模式2.6 我们到哪里了第3章 开始一个C语言模块3.1 具有可测性的C模块的那些

《测试驱动的嵌入式C语言开发》——第1章测试驱动开发

第1章 测试驱动开发我们都做过这样的事--写一大堆代码然后艰难地使它工作起来.也就是先建造再修正.测试是在代码写完之后的事情.测试总是一件后面加上来的事情,这也是我们过去唯一所知的方法.这种很难预料的过程被亲切地称为"调试"(debugging),我们可能会在其中花掉半个小时.调试的过程在我们的进度中被"测试"和"集成"粉饰起来.它总是风险和不确定的来源.修改一个bug可能导致产生另一个,有时是一系列其他的bug.我们往往会统计这些数据来预测把b

《测试驱动的嵌入式C语言开发》——2.3节CppUTest:一个用C++实现的自动化单元测试框架

2.3 CppUTest:一个用C++实现的自动化单元测试框架现在你已经见过了Unity,接下来我会快速介绍一下CppUTest,同时也是我更倾向于使用的对C和C++代码进行单元测试的自动化测试框架.事实上,不仅因为它是一个功能全面的测试框架,同时也因为我是CppUTest的作者之一.本书开始的几个例子会用Unity,在第8章之后会使用CppUTest.CppUTest是为了支持在多种操作系统上开发嵌入式软件而特别设计的.CppUTest的宏被设计成不需要了解C++也可以写测试用例.这使得C程序

《测试驱动的嵌入式C语言开发》——第3章开始一个C语言模块

第3章 开始一个C语言模块在本章里,我会带你浏览用测试驱动来开发一个新的C模块首先要经历的那些步骤.在第4章里,我们则会全速前进来完成这个模块.从这一章开始并且贯穿本书,我们会关注到底能不能实现Dijkstra的不引入bug的愿景.我们所用的工具就是TDD.

《测试驱动的嵌入式C语言开发》——1.6节对于嵌入式开发的益处

1.6 对于嵌入式开发的益处嵌入式软件开发面临所有"通常意义上"的软件开发的挑战.例如很难把进度计划做得好且可靠.但嵌入式软件开发也有其自身特有的更多挑战.这并不意味着嵌入式开发不能采用TDD.嵌入式开发者最常引用的借口是嵌入式代码依赖于硬件.依赖关系对于非嵌入式代码也是个大问题.幸运的是,我们有办法来解决这些依赖问题.原则上讲,对硬件设备的依赖和对数据库的依赖没什么区别.嵌入式开发者面临很多挑战,我们将展开讨论如何从TDD借力.嵌入式开发者不仅能收到前面讲过的那些非嵌入式开发者能享受

《测试驱动的嵌入式C语言开发》——3.5节先测试驱动接口再测试驱动内部实现

3.5 先测试驱动接口再测试驱动内部实现好的接口对于设计良好的模块来讲很关键.前面几个测试会驱动接口设计.关注于接口意味着我们是从外向内开发代码的.测试作为接口的首个用户,从调用者(或客户端代码)的角度给出了开发代码的使用方式.从使用者的角度出发会产生可用性更强的接口.我通常也会让前面的几个测试来检验一些产品代码的边界条件.选择一个带边界检查的简单用例. 为了消除这个编译错误,在模块的接口声明头文件中增加这个接口函数原型: 写出并且通过这些测试能帮助我们实现以下目标:它定义了驱动程序的一个接口函

《测试驱动的嵌入式C语言开发》——3.3节写一个测试列表

3.3 写一个测试列表在开发新功能之前先创建一个测试列表会很有帮助.测试列表由需求衍生而来.测试列表定义了你对将需要完成的功能的最好的理解.这个列表不需要很完美.它只是个临时的文档,可能只记在一张卡片上或者笔记本上.你也可以直接把它当做注释输入到测试文件中.随着每个测试的添加,对应的注释将被删除.不要在写作这个列表上花太多时间,对于LED驱动程序来讲,花几分钟即可.我的初始测试列表如图3-1所示.在我们开始之前,先列出我们都需要测试什么. 请当心在创建测试列表时的报酬递减(diminishing

《测试驱动的嵌入式C语言开发》——2.6节我们到哪里了

2.6 我们到哪里了到这里,你应当对Unity和CppUTest的概况有了很好的了解,并且明白了如何用测试夹具和测试用例来定义测试.到目前为止你还没有看到测试驱动开发(TDD),为sprintf()写的那些测试并不是TDD,因为sprintf()是已经存在的代码.请你把新学到的知识运用到接下来的练习中.在接下来的两章中我们会用测试驱动来开发一些新代码.学以致用 在开发平台上建立一个测试环境.你可以从附录A中得到一些帮助.下载书中的代码,并运行makefile.可以访问本书的主页找到书中的代码:

《测试驱动的嵌入式C语言开发》——3.10节学以致用

学以致用 重新编写一个你自己的LedDriver,以便在第4章中使用.你可以在code/SandBox中找到开始所需的文件.其中有一个README.txt文件可以参考. 为一个内容为整型的先入先出的环状缓冲CircularBuffer写一个测试列表. 开始测试驱动开发CircularBuffer.选择检查初始状态并使用其接口的那些测试来做TDD.选择那些可以只用硬编码返回值就能通过的测试.你会需要修改makefile让它能够找到CircularBuffer的文件.在第4章之后你将会有机会完整地实