嵌入式软件测试

如何在目标板上实时测试应用程序

为什么嵌入式系统测试困难?
 


在目标板上测试面临的系列问题:

1、如何下载测试到板子上,然后如何收集测试结果

2、如何累积可重复自动执行的测试

3、如何尽可能减少人工工作

4、如何减少内存不够的问题

       这些都是经常碰到但难以解决的问题。随着项目代码越来越大,开发人员数量和代码数量都变多,完全懂得目标硬件和软件工作原理的可能仅限于少数工程师。大多数项目中的工程师面对复杂的系统甚至不知如何在目标设备上执行测试。

测试什么代码?

       解决问题的一种方案是在主机端进行软件测试。对于那些跟硬件或者交叉编译无关的代码,使用主机平台的编译器构建可执行测试程序然后执行是可行的,这可以一定程度找到一些逻辑错误,但对有些与硬件相关的问题却无能为力。不同的编译器,不同的处理器,代码执行表现可能存在某些差异,实际上,这也是为什么对于高安全性软件的测试需要尽可能在最终环境上进行测试。这意味着在你的测试环境中应该使用与软件产品环境一致的构建工具和处理器。

       除非你直接测试配置到嵌入式设备上的目标代码(Object Code),你不能确保不会引入诸如编译器漏洞,数据对齐以及大小端问题。


1.单元测试的必要性

单元测试是软件开发的重要一环,尤其对嵌入式开发。因为嵌入式开发受限于开发环境、调试工具等因素,不能和纯PC软件开发一样使用很多先进的工具。这就需要开发者在开发过程中,进行更细的模块划分,更明确的接口,更详尽的测试。

传统的开发先出设计方案,然后跟着写出代码,最后在做测试---常被叫做bug调试,所以在代码“写完”之后,还有一半左右的开发量。人都会犯错,在设计和编码中也会犯错,如果后期调试编程去解决设计和编码中引入的错误,那么可能已经过了几天几周几个月,反馈如此只晚,已经不能从错误中吸取经验了,下次还会犯同样的错误。此外根据软件工程理论,1个bug越是在后期越是花费巨大的成本去修复,并且随着系统复杂度的增长,在一个大的系统中去查找某一个细节具体的问题,相比于在小的模块中去查找问题会花费多倍的时间成本。

2.单元测试框架解剖

一般地单元测试需要实现以下几个基本功能:
1. assert
各种assert,比如AssertTrue、比如AssertFail、AssertStrEquals、AssertIntEquals......
条条大路通罗马,这些Assert有各种功能,其实就是包装了断言的函数。比如AssertStrEquals(str, "open"),进行str和字符串“open”的比较,如果不相同则会报错。
根据框架的结构,在assert失败时候,有的进行长跳转longjmp,有的对类似failCount的全局的变量进行加1并记录错误位置。
2. 错误位置记录
得益于C语言的LINE、FILE宏,这是2个ANSI C标志支持的内置宏定义,可以得到当前的的行数和文件名。 在断言失败的地方,记录文件名和行号,以供用户查询错误的位置。

1

2

char buf[HUGE_STRING_LEN];

sprintf(buf, "%s:%d: ", _FILE_, _LINE_);

3. 测试case管理
这是测试框架区别于自己写的assert测试函数最根本的地方。 测试框架为了提高函数利用率,减少重复,方便测试例程汇总等,都会进行各种封装。比如以下几条。
1)setup和teardown
大部分的测试框架都提供这两个函数,主要是因为有些测试case,有大量重复的代码,比如准备输入数据,测试完毕后清理现场等通用的功能。
2)测试例子汇总
有的叫做TestSuit,有的叫做TestFixtures。把一类相似功能的测试case进行汇总,方便更高层次的调用,也方便用户管理测试例程。
3)测试的调用
多个测试例程汇总后,构成一个数组(表格),启动运行,一般由xxxRun函数负责。
在嵌入式c中,一般都有一个函数指针来操作,这也是为什么所有的测试case的函数名称都使用相同的声明,test_case需要和调用该测试的指针同类型。

4. 测试的执行
测试的执行本质就是函数的长跳转。可以看做在父函数中调用子函数,这个子函数如果是测试例程的话,子函数就会包含assert相关的语句,而assert语句在出错后,会记录错位位置和错误消息,然后进行长跳转(longjmp),longjmp和setjmp(buf)成对出现,返回到调用的位置,然后进行下一个测试case。

    for (i = 0 ; i < testSuite->count ; ++i)
    {
        Test* testCase = testSuite->list[i];
        TestRun(testCase);
        if (testCase->failed) {
        testSuite->failCount += 1; 
        }
    }

 

3. 测试框架的本质

1)为了更好的组织测试,提供的测试组的批量处理功能,一般由for循环遍历一个table数组实现;
2)为了减少重复进行测公用函数提取,比如准备测试环境和清理现场;
3)测试需要的各种断言;
4)断言失败后的跳转、记录错误位置-FILE-, -LINE-宏的使用;
5)测试case运行的监控和结果的汇总。


综上,如果你实现了上面的几个功能,那么也就自己完成了一个测试框架。
其实测试框架是一个很简单的事情,如今测试框架有很多,像VS这样的IDE已经集成了单体测试,所以对于一个开发者怎么规划测试才是测试工作的第一要务。
如何恰当的写测试用例,既不延误开发又不会造成工程臃肿,还能尽可能的覆盖测试范围,这才是测试中最花费功夫的地方。


测试涵盖设计文案,实现方式,代码框架,需求分析。

静态测试:阅读,查阅,学习需求文档,包括市场已有相关产品的说明手册。通过学习理解设计的初衷。并且对需求进行分析,拆分成测试用力。

动态测试:分阶段进行代码review,白盒测试,已经最后的产品黑盒测试。

嵌入式测试框架中的一个特点在开始就是一场探索性的测试方式。“探索性”的思考,“探索性”的测试行为贯穿于整个流程中。探索性测试在嵌入式测试的过程中并不等同于free test;也不是没有计划的冒险性测试。需要有测试基础知识和方法论的,制定符合项目阶段需求的测试计划,并且有效实施。

第一步:在需求分析阶段设计,学习框架。

这里提到的框架是产品设计框架。它将决定测试的内容是在产品实现的每个阶段,研发需要给测试提供的接口。

嵌入式软件开发案例

一个常规嵌入式软件开发架构包括下面几个模块:

1. 通讯协议

2. 实施系统

3. 控制装置

4. 事件处理

5. PC端的工具

6. 硬件配置

测试需要搞清楚这些模块之间的关系,任务内容,包括这些模块之间的通讯方法之后才能算对产品系统有一定的了解,之后进一步了解产品需求分析奠定好基础。

第二步:详细学习需求手册,学习原型代码结构

为什么要学习代码结构?代码结构中包含了对数据最直观的定义,比需求分析里面的数据解释清楚很多。

例如:需求手册里一句:用户通过网线连接装置后通过定义好的通讯协议完成数据发放。

就这一句话,测试需要查阅:通讯模块中的传送函数。在这个接口中将定义出传送数据的数据结构。有代码意识的测试人员看数据结构定义比看文档里面的文字定义速度快。另外一个好处,帮助研发完成了一个内部的code review。

第三步:在原型扩展的阶段,配合研发实现unit test。

理论上unit test是由研发自己完成的纯白盒测试。某论坛上的实践提议是:在研发人员动手写方法函数之前,需要完成各自方法函数的unit test方法,之后通过自己搭建的unit test框架结构来实现方法,任何的code变量都会因为unit test的存在及时地反馈给研发人员,从而作到高质量的控制。然后实际过程当中,我们多半遇到的是一堆函数接口,没有文档,没有code comments,没有unit test,这个时候怎么办?测试人员在前面两步的基础上,可以在这里帮助研发完成一部分unit test。主要考虑全局变量的稳定性;参数范围的准确性和一部分代码强壮性的测试。

第四步:探索性方法测试法去完成功能测试和集成测试

这部分本身需要研发来实现必要的测试接口,但是主要测试目的还是验证方法实现与否。最好的情况是,在代码级别又测试人员设计接口参数和返回值定义。在执行时,提供对应的测试接口运行方式。为何要说是探索性的测试方法,因为考虑到测试人员的代码能力和对产品的需求学习是否正确。那么在过程中,需要进行一步步探索的方式,吃透黑盒需求,搞清楚代码关系,才能完美的完成嵌入式级别代码的方法测试。

例如:状态机测试。状态机,顾名思义,功能而言就是通过状态变化完成信息透传。对于没有架构观念的人直接会跳过状态机功能测试,直接实现所谓的系统测试,就是通过配置工具直接作用于底层产品的功能测试,这样虽然节约时间,但是架空的状态机的测试,最终出现问题时会无法定位问题的根本。

只有把整个产品分模块吃透功能测试之后,进行集成管理发布的流程,才能说,此时发布的产品是可以用于进行黑盒系统测试的产品。

第五步:黑盒测试法去完成系统测试

这部分实际工作当中需要分别出:verification和validation。为何要分清楚。只有找到完全负责的对象之后,测试行为才能找到重心。Verification的负责对象是研发,即使走黑盒测试,主要目的是看代码功能是否完成和基本数据流程是否传递并工作。Validation的负责对象是用户,走黑盒测试的目的主要是模拟用户使用产品流程,不带破坏性操作的情况下完成系统的性能测试和强度(健壮性)测试。这部分的case的生成依赖第二和第四步。问题来了,在黑盒测试阶段我们依据工具完成嵌入式测试吗?是的,需要依附其他工具。现在每一套嵌入式的工具都会携带一部分在PC或者移动设备上的交互软件实现操作,也有一部分直接携带HMI,那么我们测试时还需要其他工具吗?需要!这些工具是这一整套产品,并不是用来给测试人员操作测试核心对象的。说白了,这些配置产品也是测试对象。在研发团队中需要有人来维护对应的测试工具。这样的测试工具可以作为配置设备的测试对比。

第六步:自动化测试计划完成维护测试,性能测试

能走自动化测试团队是比较厉害的。在短周期项目中,自动化测试部分只能作用于维护,而对于长周期项目来说,自动化是可以期待回归测试作用的。本身源于PC端,一直辅助功能的技术在嵌入式测试流程中也只能体现出辅助的功能。可以依据产品属性来定义自动化测试的存在与否。当产品周期短,实现产品内容繁多,操作复杂就不建议使用自动化测试。自动化测试方法的启动取决于测试对象,测试工具的发展。并不是团队自动化技能有否。

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值