单元测试
本文最后更新于29 天前,其中的信息可能已经过时

概念

  • 单元测试通常独立于其他测试进行,不依赖于外部环境和系统状态。
  • 是针对代码层面进行的测试,是最严格的软件测试手段。
  • 可以在软件开发的早期以最小的成本保证局部代码的质量。
  • 单元测试也是自动化测试的重要内容,通常以自动化的方式执行

单元测试的实践

代码的基本特征

  • 无论是哪种开发语言,都是使用条件分支、循环处理、函数调用等基本逻辑控制。
  • 如果只查看代码结构,可以发现几乎所有的代码都是对数据进行分类处理

代码产生错误的原因

  • 代码分类遗漏
  • 代码分类错误
  • 代码分类的处理逻辑错误

单元测试用例

单元测试用例的“输入数据”

  • 被测函数的输入参数
  • 被测函数内部需要读取的全局静态变量
  • 被测函数内部需要读取的成员变量
  • 在函数内部调用子函数获得的数据
  • 在函数内部调用子函数改写的数据
  • 嵌入式系统中,在终端调用时改写的数据
  • ……

单元测试用例的“预计输出”

  • 被测函数的返回值
  • 被测函数的输出参数
  • 被测函数所改写的成员变量
  • 被测函数所改写的全局变量
  • 被测函数中进行的文件更新
  • 被测函数中进行的数据库更新
  • 被测函数中进行的消息队列更新
  • 被测试函数中调用的其他函数
  • ……

驱动代码、桩代码和Mock代码

驱动代码、桩代码和Mock代码时单元测试中最常出现的3个名词

三者的逻辑关系

驱动代码

指调用被测参数的代码

在单元测试中,驱动模块通常包括 调用被测函数前的数据准备、调用被测函数、验证 三个相关步骤
驱动代码的结构通常由单元测试的框架决定

桩代码

用来代替真实代码的临时代码,模拟某些函数的内部调用,如下为函数A内部调用未实现的函数B

//被测函数A内部调用未实现的函数B
void funA()
{
    boolean funB_returnValue;
    funB_returnValue = funB();
    if ( ture == funB_returnValue )
         { do Operation 1; }
    else if ( false == funB_returnValue )
         { do Operation 2; }
}

在单元测试阶段,由于函数B尚未实现,为了不影响对函数A实现逻辑的测试,可以用一个假的函数B来代替未实现的函数B,这个假的函数B就是桩函数。
为了实现函数A的全路径覆盖,需要控制不同的测试用例中函数B的返回值。桩函数B的伪代码如下所示

//桩函数B的伪代码
void funB()
{
    if (testcaseID == 'TC00001')
    {
        return true;
    }
    else if (testcaseID == 'TC00002')
   {
        return false;
   }
}

当执行测试用例TC00001时,返回值为true,当执行测试用例TC00002时,返回值为false。这样就完成了对被测函数A的分支全覆盖。

从上述例子可以看出,桩代码的应用首先起到了隔离和补充的作用,使被测代码能够独立编译、链接,并独立运行。同时,桩代码还具有控制被测函数执行路径的作用。

桩代码的编写

  • 桩函数要具有与原函数完全相同的原型,仅仅内部实现不同
  • 用于实现隔离和补充的桩函数比较简单,只须保持原函数的声明,并加一个空的实现即可,目的是通过编译和链接
  • 实现控制功能的桩函数是应用最广泛的,要根据测试用例的需要,输出合适的数据作为被测函数的内部输入

Mock代码

Mock代码和桩代码非常类似,都是用来代替真实代码的临时代码,起到隔离和补充的作用

桩代码的侧重点时利用桩来控制被测函数的执行路径,不会关注桩是否已经怎样调用。所以,在使用桩的测试中,对结果的验证,通常出现在驱动代码中

相比于桩代码,Mock代码更关注于Mock方法有没有被调用,以什么样的参数调用,调用的次数,以及多个Mock函数的先后调用顺序。所以,在使用Mock代码的测试用,对结果的验证(也就是断言),通常出现在Mock函数中

单元测试的实践原则

  • 单元测试通常只在底层模块或者核心模块中进行
  • 单元测试需要根据开发语言选择测试框架,如,Java常用的测试框架时JUnit和Test NG,C/C++时Cpptest和Parasoft C/C++ test
  • 框架选择完成后,还需要根据开发所用的技术栈对桩代码和Mock代码进行选型。
  • 通常还需要引入计算代码覆盖率的工具来衡量单元测试的代码覆盖率。如Java的JaCoCo、JavaScript的Istanbul。
  • 需要对单元测试的执行、代码覆盖率的统计和持续集成的流水线进行集成,以确保每次提交代码,都会自动触发单元测试,并在单元测试的执行过程中自动统计代码覆盖率,最后以“单元测试通过率”和“代码覆盖率”为标准来决定本次提交代码是否能够被接受

单元测试过程中的难点

  • 紧密耦合的代码难以隔离
  • 隔离后编译、链接、运行困难
  • 代码本身的可测试性较差,通常代码的可测试性与代码规模成正比
  • 无法通过桩代码直接模拟系统底层函数的调用
  • 代码覆盖率越往后越难提高
文末附加内容
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇