单元测试详述

单元测试是对软件中最小可测试单元进行验证的过程,如函数或类。它有助于早期发现问题,减少集成和系统测试的工作量。通过创建驱动代码、桩代码和Mock代码来实现对代码逻辑的独立验证。单元测试关注正常及边界输入,预期输出,并使用自动化方式执行。为了有效开展单元测试,应选择合适的测试框架,关注核心和底层模块,结合代码覆盖率工具,并与持续集成流程整合。
摘要由CSDN通过智能技术生成

什么是单元测试

举一个例子–工厂生产电视机
工厂首先将各电子元器件组装成各模块电路板,然后再将这些电路板组装起来构成一个完整的电视机。
正常的话,接通电源后,就可以观看电视节目了。但大多数情况下组装完成的电视机根本无法开机,这时就需要把电视机拆开,然后逐个模块排查问题。
假设你发现是供电板的供电电压不足,那你就要继续逐级排查组成供电板的各个电子元器件,最终你可能发现罪魁祸首是一个电容的故障。这时,为了定位到这个问题,你已经花费了大量的时间和精力。
那在后续的生产中,如何才能避免类似的问题呢?
很容易想到,为什么不在组装之前,先将各电子元器件测试一下,就可以避免组装后的一些列问题。
如果把电视机的生产、测试和软件的开发、测试进行类比:
电子元器件就像是软件中的单元,通常是函数或者类,对单个元器件的测试就像是软件测试中的单元测试;
组装完成的功能电路板就像是软件中的模块,对电路板的测试就像是软件中的集成测试;
电视机全部组装完成就像是软件完成了预发布版本,电视机全部组装完成后的开机测试就像是软件中的系统测试。
那么,单元测试到底是什么呢?
单元测试是指,对软件中的最小可测试单元在与程序其他部分相隔离的情况下进行检查和验证的工作,这里的最小可测试单元通常是指函数或者类。
单元测试通常由开发工程师完成

单元测试的好处

1.单元测试属于最严格的软件测试手段,是最接近代码底层实现的验证手段,可以在软件开发的早期以最小的成本保证局部代码的质量。
2.单元测试都是以自动化的方式执行,所以在大量回归测试的场景下更能带来高收益。
3.单元测试的实施过程还可以帮助开发工程师改善代码的设计与实现。在单元测试代码里提供函数的使用示例,因为单元测试的具体表现形式就是对函数以各种不同输入参数组合进行调用,这些调用方法构成了函数的使用说明。

如何做好单元测试

  1. 代码的基本特征与产生错误的原因

1)代码的基本特征,所有的代码都是在对数据进行分类处理。
每一次条件判定都是一次分类处理,嵌套的条件判定或者循环执行,也是在做分类处理。
2)产生错误的原因
分类遗漏;分类错误;分类无遗漏且无错误,但是处理逻辑错误。
要做到代码功能逻辑正确,必须做到分类正确并且完备无遗漏,同时每个分类的处理逻辑必须正确。
3)开发工程师为了设计并实现逻辑功能正确的代码需要考虑的
a.如果要实现正确的功能逻辑,会有哪几种正常的输入;
b.是否有需要特殊处理的多种边界输入;
c.各种潜在非法输入的可能性以及如何处理。
这些开发工程师眼中的代码“功能点”,就是单元测试的“等价类”。

  1. 单元测试的基本方法

单元测试的用例是一个“输入数据”和“预计输出”的集合。需要针对确定的输入,根据逻辑功能推算出预期正确的输出,并且以执行被测试代码的方式进行验证,用一句话概括就是“在明确了代码需要实现的逻辑功能的基础上,什么输入,应该产生什么输出”。

1)单元测试“输入数据”包含哪些
a.被测试函数的输入参数;
b.被测试函数内部需要读取的全局静态变量;
c.被测试函数内部需要读取的成员变量;
d.函数内部调用子函数获得的数据(子函数的返回值作为主函数的参数);
e.函数内部调用子函数改写的数据(被子函数修改的值作为主函数的参数);
f.嵌入式系统中,在中断调用时改写的数据;
g…
2)单元测试“预计输出”包含哪些
a.被测试函数的返回值;
b.被测试函数的输出参数;
c.被测试函数所改写的成员变量;
d.被测试函数所改写的全局变量;
e.被测试函数中进行的文件更新;
f.被测试函数中进行的数据库更新;
g.被测试函数中进行的消息队列更新;
h…

  1. 单元测试的主要技术手段

驱动代码,桩代码和 Mock 代码
在这里插入图片描述
驱动代码是用来调用被测函数的,而桩代码和 Mock 代码是用来代替被测函数调用的真实代码的。
驱动代码(Driver)指调用被测函数的代码,在单元测试过程中,驱动模块通常包括调用被测函数前的数据准备、调用被测函数以及验证相关结果三个步骤。驱动代码的结构,通常由单元测试的框架决定。
桩代码(Stub)是用来代替真实代码的临时代码。 比如,某个函数 A 的内部实现中调用了一个尚未实现的函数 B,为了对函数 A 的逻辑进行测试,那么就需要模拟一个函数 B,这个模拟的函数 B 的实现就是所谓的桩代码。
为了实现函数 A 的全路径覆盖,需要控制不同的测试用例中函数 B 的返回值,例如true、false返回,需要覆盖这两种情况。
桩代码的作用:首先起到了隔离和补齐的作用,使被测代码能够独立编译、链接,并独立运行。同时,桩代码还具有控制被测函数执行路径的作用。
编写桩代码的三个原则
a.桩函数要具有与原函数完全相同的原形,仅仅是内部实现不同,这样测试代码才能正确链接到桩函数,比如函数名相同;
b.用于实现隔离和补齐的桩函数比较简单,只需保持原函数的声明,加一个空的实现,目的是通过编译链接;
c.实现控制功能的桩函数是应用最广泛的,要根据测试用例的需要,输出合适的数据作为被测函数的内部输入。

Mock 代码和桩代码非常类似,都是用来代替真实代码的临时代码,起到隔离和补齐的作用。
mock代码和桩代码本质的区别是:测试期待结果的验证

  • 对于 Mock 代码来说,我们的关注点是 Mock 方法有没有被调用,以什么样的参数被调用,被调用的次数,以及多个 Mock 函数的先后调用顺序。所以,在使用 Mock 代码的测试中,对于结果的验证(也就是 assert),通常出现在 Mock 函数中。
  • 对于桩代码来说,我们的关注点是利用 Stub 来控制被测函数的执行路径,不会去关注 Stub 是否被调用以及怎么样被调用。所以,你在使用 Stub 的测试中,对于结果的验证(也就是 assert),通常出现在驱动代码中。

如何开展单元测试

1.并不是所有的代码都支持单元测试,仅测试核心模块和底层模块
2.单元测试框架的选型,这和开发语言直接相关。
比如,Java 最常用的单元测试框架是 Junit 和 TestNG;C/C++ 最常用的单元测试框架是 CppTest 和 Parasoft C/C++test;框架选型完成后,你还需要对桩代码框架和 Mock 代码框架选型,选型的主要依据是开发所采用的具体技术栈。
通常,单元测试框架、桩代码 /Mock 代码的选型工作由开发架构师和测试架构师共同决定。
3.为了能够衡量单元测试的代码覆盖率,通常你还需要引入计算代码覆盖率的工具。
不同的语言会有不同的代码覆盖率统计工具,比如 Java 的 JaCoCo,JavaScript 的 Istanbul。
4.最后需要把单元测试执行、代码覆盖率统计和持续集成流水线做集成,以确保每次代码递交,都会自动触发单元测试,并在单元测试执行过程中自动统计代码覆盖率,最后以“单元测试通过率”和“代码覆盖率”为标准来决定本次代码递交是否能够被接受。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值