#10.软件测试基础:单元测试

单元测试


在这里插入图片描述在这里插入图片描述
在这里插入图片描述

1、单元与单元测试的概念

什么是单元?

用面向过程语言(如C、Visual Basic)设计的软件,单元可以是一个或若干个函数或过程所组成
用面向对象语言(如Java、C++)设计的软件,单元可以是一个类或类的实例,或者由方法来实现的功能

单元并不单只一个具体函数或一个类的方法。
在这里插入图片描述

什么是单元测试?——定义

单元测试:是对软件基本组成单元进行的测试。是检验程序最小单位,即检查模块有无错误,它是在编码完成后必须进行的测试工作。

需要关注的问题:

目标

  • 确保每个模块能正常工作

时间

  • 编码—编译—单元测试

注意

  • 前期完成单元测试计划、设计好用例

依据

  • 详细设计说明

执行者

  • 程序开发者或白盒测试人员

如何操作

  • 以白盒测试法为主,先静态检查分析代码是否符合规范,再动态运行代码,检查结果。

目的:执行单元测试,是为了证明某段代码的行为确定和开发者所期望的一致。
时间:编码—编译—单元测试(静态分析+动态运行)
TDD(Test-Driven Development):测试驱动开发是敏捷开发中的一项核心实践和技术,也是一种设计方法论。TDD的原理是在开发功能代码前,先编写单元测试用例代码,测试代码确定需要编写什么产品代码。
依据:单元测试的依据是详细设计描述,单元测试应对模块内所有重要的控制路径设计测试用例,以便发现模块内部的错误。
执行者:开发人员,因而单元测试大多是从程序内部结构出发设计测试用例

测试方法:以白盒测试法为主(覆盖),先静态检查代码是否符合规范,再动态运行代码,检查结果。当然验证结果正确是前提,还要检查很多项,如:程序的容错能力、程序的边界值处理等
静态:在代码无错误编译,但不符合规范,就像人写的东西能被理解,但不符合语法规则 另含走查等
好处:可靠性、可读性维护性更强、移植性(移到另一个硬件环境或编译器)
如:缩进、空格、对齐、空行、注释

3、单元测试的主要任务

单元测试针对每个程序的模块,主要测试如下5方面:
模块接口、局部数据结构、边界条件、独立的路径和错误处理。

模块接口:

对模块接口进行测试,检查进出程序单元的数据流是否正确。须在其它测试之前进行。
测试重点:
调用所测模块时的输入参数与模块的形式参数在个数、属性、顺序上是否匹配;
所测模块调用子模块时,它输入给子模块的参数与子模块中的形式参数在个数、属性、顺序上是否匹配;
是否修改了只做输入用的形式参数;
调用标准函数的参数在个数、属性、顺序上是否正确;
全局变量的定义在各模块中是否一致。
注:主要关注单元中的输入和输出。

  • ?感觉更像是白盒的静态代码测试。
局部数据结构:

测试模块内部的数据能否保持完整性,包括内部数据的内容、形式及相互关系不发生错误。
常见错误:
不正确的或不一致的类型说明。
错误的初始化或默认值。
错误的变量名,如拼写错误或书写错误。
下溢、上溢或者地址错误。
注:主要关注与被测单元内部的相关数据的类型。

路径测试:

针对程序路径进行测试,尤为重要。重点关注由于计算错误、不正确的判定或不正常的控制流而产生的错误。
常见错误:
误解的或不正确的算术优先级;混合模式的运算;错误的初始化;精确度不够精确;表达式的不正确符号表示;不同数据类型的比较;不正确的逻辑操作或优先级;应当相等的地方由于精确度的错误而不能相等;不正确的判定或不正确的变量;不正确的或不存在的循环终止;当遇到分支循环时不能退出;不适当地修改循环变量。
注:主要关注程序的逻辑分支问题。

  • 这里应该是白盒的控制流、数据流测试了。
边界条件:

采用边界值分析方法设计测试用例,重点关注程序边界处。
常见边界:
可能与边界有关的数据类型如数值、字符、位置、数量、尺寸等。
边界的首个、最后一个、最大值、最小值、最长、最短、最高、最低等特征。如:运算或判断中取最大值、最小值时是否有错误。
在n次循环的第0次、1次、n次是否有错误。
数据流、控制流中刚好等于、大于、小于确定的比较值是否出现错误。
注:主要针对于单元测试中的边界问题。

  • 很明显契合 边界值测试。
出错处理:

重点关注模块在工作中发生错误时,出错处理设施是否有效。
常见错误:
对运行发生的错误描述难以理解。
所报告的错误与实际遇到的错误不一致。
出错后,在错误处理之前就引起系统的干预。
例外条件的处理不正确。
提供的错误信息不足,以至于无法找到错误的原因。
注:主要针对程序中的错误提示。

3、单元测试的执行过程

单元测试时,如果模块不是独立的程序,需要设置一些辅助测试模块。
辅助测试模块类型:驱动模块和桩模块
在这里插入图片描述

实例
// demo5.cpp : 定义控制台应用程序的入口点。
#include “stdafx.h”
int _tmain(int argc, _TCHAR* argv[])
{
	int a = 4;
	int b = 2;
	int c;
	c = add(a,b);
	printf("a+b=%d\n",c);
	c = sub(a,b);
	printf("a-b=%d\n",c);
	c = mlt(a,b);
	printf("a*b=%d\n",c);
	c = div(a,b);
	printf("a/b=%d\n",c);
	return 0;
}

演示中的 main函数就是驱动模块
驱动是什么意思:汽车的驱动 引导动起来

那什么是桩模块 如果说一个函数处于中间位置 既要被别人调用 也要调用其他的模块 如果调用了很多函数的话 是不是要把被测函数和 其调用的函数都测试了呢?
当然不是的 工作量太大了 是吧 那怎样解决呢 就用到了桩模块

演示2:先分析程序意思 然后看“如果对加减乘除 4个函数进行测试的话 谁是驱动模块 ;再考虑 除法函数 谁是桩模块” 另外在这里强调“模拟”

需要咱们来写的 驱动模块和桩模块都是额外的开销,虽然在单元测试中必须编写,但并不需要作为最终的产品提供给用户。

被测模块、驱动模块和桩模块——“单元测试的环境”:

在这里插入图片描述
如果被测模块是一个类的一个方法,驱动要完成:类生成了一个实例的对象,以及这个方法所调用的参数等
驱动函数就是要用来做火车头的,当被测函数不能直接运行时,就需要一个驱动其运行的函数,比如说main(),或者别的可以将这个函数运行起来以便于你来测试的函数。
桩:是对外的状态的改变 比如有一些输出值,则要模拟对象接收输出值。

4、单元测试实例

企业中如何进行单元测试或白盒测试?
直接使用main函数调用测试代码。
借助测试工具执行测试代码。
常用工具:

  • c++Test;(C++)
  • JUnit;(Java)
  • NUnit;(.NET)

5、单元测试前的预备知识

设计测试用例:测试脚本+断言(assert)。

断言:若干个方法,用来判断某个语句的结构是否和预期相符。
如:JUnit断言是JUnit提供的进行测试的函数。
assertEquals([String message], expected, actual)
assertNull([String message],java.lang.Object object)
assertSame( [String message], expected,actual)
assertTrue( [String message],boolean condition)
assertFalse( [String message],boolean condition)
fail( String message )

测试步骤

C++Test

  • 1、下载安装;
  • 2、read symbols:读入程序中所有符号;
  • 3、build test:读入所有符号并且编译程序;
  • 4、执行静态测试(或者动态测试)。

JUnit:

  • 1、安装,可以选择独立的junit安装,也可以用ide中的插件;
  • 2、针对被测代码或者被测功能点,先创建测试类,然后在类里面创建一个个的测试方法。
    被测代码
    在这里插入图片描述
    它的测试类
    在这里插入图片描述
    被测代码 要测试的上司计算器的加法函数 ,测试类calculatorTest 它继承于TestCase类,测试类里面的testAdd就是测试方法。
  • 3、具体如何创建类及方法。
    创建测试类—位置
    单独建立测试源代码文件夹,不要和开发源代码放一起
    测试包名最好和开发包名相同或类似
    创建测试类—原则
    导入JUnit类库,继承于TestCase类
    创建测试方法—原则( JUnit3.8 )
    public void 方法名必须以test开头(无方法参数)
    在这里插入图片描述
    在这里插入图片描述
    junit的使用并不很难,然而要书写一个好的TestCase却并非易事。一个不好的TestCase往往是既浪费了时间,也起不了实际的作用。相反,一个好的TestCase,不仅可以很好的指出代码中存在的问题,而且也可以作为代码更准确的文档,同时还在持续集成的过程中起非常重要的作用。
    在此给出书写TestCase时需要注意的几点:
  • - 测试的独立性:一次只测试一个对象,方便定位出错的位置。这有2层意思:一个TestCase,只测试一个对象;一个TestMethod,只测试这个对象中的一个方法。
  • - 给测试方法一个合适的名字。
  • - 在assert函数中给出失败的原因,如:assertTrue( “… should be true”, ……),方便查错。在这个例子中,如果无法通过assertTrue,那么给出的消息将被显示。在junit中每个assert函数都有第一个参数是出错时显示消息的函数原型。
  • - 测试所有可能引起失败的地方,如:一个类中频繁改动的函数。对于那些仅仅只含有getter/setter的类,如果是由IDE(如Eclipse)产生的,则可不测;如果是人工写,那么最好测试一下。
  • - 在setUp和tearDown中的代码不应该是与测试方法相关的,而应该是全局相关的。如针对与测试方法A和B,在setUp和tearDown中的代码应该是A和B都需要的代码。
  • - 测试代码的组织:相同的包,不同的目录。这样,测试代码可以访问被测试类的protected变量/方法,方便测试代码的编写。放在不同的目录,则方便了测试代码的管理以及代码的打包和发布。一个例子如下:
public class calculator 
{
   public int add(int a,int b)
   {
   	return a+b;
   }
   public int minus(int a,int b)
   {
   	return a-b;
   }
   public int multiply(int a,int b)
   {
   	return a*b;
   }
   public int divide(int a,int b)
   {
   	return a/b;
   }
}

在这里插入图片描述
在这里插入图片描述

assert(断言)

Assert:单元测试中动态测试
若干个方法,用来判断某语句的结构是否预期相符。
流程:
被测程序
设计测试用例
测试代码(assert)
执行

JUnit、NUnit、C++ Test
常用的断言方法介绍:
assertEquals([String message], expected, actual)
assertNull([String message],java.lang.Object object)
assertSame( [String message], expected,actual)
assertTrue( [String message],boolean condition)
assertFalse( [String message],boolean condition)
fail( String message )

6、内容总结

单元与单元测试。
单元测试
编译——静态测试(规范)——动态测试(用例 如:assert)
主要任务:
模块接口、局部数据结构、边界条件、独立的路径和错误处理。
执行过程:
驱动模块:模拟被测模块的上级模块,相当于主程序。
桩模块:模拟被测模块所调用的模块。
测试实例:
C++ Test、JUnit、NUnit
ASSERT:若干方法,判断某语句结构是否和预期相符。

THE END

NO!
actually……
TO BE CONTINUED…

  • 2
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值