zoukankan      html  css  js  c++  java
  • 实验五 @单元测试@

    一、实验目的

    1、掌握单元测试的方法;

    2、学习XUnit测试原理及框架;

    3、掌握使用测试框架进行单元测试的方法和过程;

    二、实验内容与要求

    1.了解单元测试

    1.1单元测试的原理

      单元测试(unit testing),是指对软件中的最小可测试单元进行检查和验证。对于单元测试中单元的含义,一般来说,要根据实际情况去判定其具体含义,如C语言中单元指一个函数,Java里单元指一个类,图形化的软件中可以指一个窗口或一个菜单等。总的来说,单元就是人为规定的最小的被测功能模块。单元测试是在软件开发过程中要进行的最低级别的测试活动,软件的独立单元将在与程序的其他部分相隔离的情况下进行测试。单元测试是由程序员自己来完成,最终受益的也是程序员自己。可以这么说,程序员有责任编写功能代码,同时也就有责任为自己的代码编写单元测试。执行单元测试,就是为了证明这段代码的行为和我们期望的一致。
    单元测试的内容包括

    • 模块接口测试
    • 局部数据结构测试
    • 路径测试
    • 错误处理测试
    • 边界测试
    (1)模块接口测试

      模块接口测试是单元测试的基础。只有在数据能正确流入、流出模块的前提下,其他测试才有意义。模块接口测试也是集成测试的重点,这里进行的测试主要是为后面打好基础。测试接口正确与否应该考虑下列因素

    • 输入的实际参数与形式参数的个数是否相同
    • 输入的实际参数与形式参数的属性是否匹配
    • 输入的实际参数与形式参数的量纲是否一致
    • 调用其他模块时所给实际参数的个数是否与被调模块的形参个数相同;
    • 调用其他模块时所给实际参数的属性是否与被调模块的形参属性匹配;
    • 调用其他模块时所给实际参数的量纲是否与被调模块的形参量纲一致;
    • 调用预定义函数时所用参数的个数、属性和次序是否正确;
    • 是否存在与当前入口点无关的参数引用;
    • 是否修改了只读型参数;
    • 对全程变量的定义各模块是否一致;
    • 是否把某些约束作为参数传递。

      如果模块功能包括外部输入输出,还应该考虑下列因素:

    • 文件属性是否正确;

    • OPEN/CLOSE语句是否正确;

    • 格式说明与输入输出语句是否匹配;

    • 缓冲区大小与记录长度是否匹配;

    • 文件使用前是否已经打开;

    • 是否处理了文件尾;

    • 是否处理了输入/输出错误;

    • 输出信息中是否有文字性错误。

    • 局部数据结构测试;

    • 边界条件测试;

    • 模块中所有独立执行通路测试;

    (2)局部数据结构测试

      检查局部数据结构是为了保证临时存储在模块内的数据在程序执行过程中完整、正确,局部功能是整个功能运行的基础。重点是一些函数是否正确执行,内部是否运行正确。局部数据结构往往是错误的根源,应仔细设计测试用例,力求发现下面几类错误:

    • 不合适或不相容的类型说明;

    • 变量无初值;

    • 变量初始化或省缺值有错;

    • 不正确的变量名(拼错或不正确地截断);

    • 出现上溢、下溢和地址异常。

    (3)边界条件测试

      边界条件测试是单元测试中最重要的一项任务。众所周知,软件经常在边界上失效,采用边界值分析技术,针对边界值及其左、右设计测试用例,很有可能发现新的错误。边界条件测试是一项基础测试,也是后面系统测试中的功能测试的重点,边界测试执行的较好,可以大大提高程序健壮性。

    (4)独立路径测试

      在模块中应对每一条独立执行路径进行测试,单元测试的基本任务是保证模块中每条语句至少执行一次。测试目的主要是为了发现因错误计算、不正确的比较和不适当的控制流造成的错误。具体做法就是程序员逐条调试语句。常见的错误包括:

    • 误解或用错了算符优先级;

    • 混合类型运算;

    • 变量初值错;

    • 精度不够;

    • 表达式符号错。

    (5)错误处理测试

      检查模块的错误处理功能是否包含有错误或缺陷。例如,是否拒绝不合理的输入;出错的描述是否难以理解、是否对错误定位有误、是否出错原因报告有误、是否对错误条件的处理不正确;在对错误处理之前错误条件是否已经引起系统的干预等。

      通常单元测试在编码阶段进行。在源程序代码编制完成,经过评审和验证,确认没有语法错误之后,就开始进行单元测试的测试用例设计。利用设计文档,设计可以验证程序功能、找出程序错误的多个测试用例。对于每一组输入,应有预期的正确结果。

    1.2 测试框架

      xUnit是各种代码驱动测试框架的统称,这些框架可以测试 软件的不同内容(单元),比如函数和类。xUnit框架的主要优点是,它提供了一个自动化测试的解决方案。可以避免多次编写重复的测试代码。底层是xUnit的framwork,xUnit的类库,提供了对外的功能方法、工具类、api等

      TestCase(具体的测试用例)去使用framwork

      TestCase执行后会有TestResult

      使用TestSuite控制TestCase的组合

      TestRunner执行器,负责执行case

      TestListener过程监听,监听case成功失败以及数据结果,输出到结果报告中

    1.3面向特定语言的,基于xUnit框架的自动化测试框架

      Junit : 主要测试用Java语言编写的代码

      CPPunit:主要测试用C++语言编写的代码

      unittest , PyUnit:主要测试用python语言编写的代码

      MiniUnit: 主要用于测试C语言编写的代码

    三、实验过程

    3.1 程序源码

     3.2 介绍以及安装unittest测试框架

        由于程序代码是Python语言,代码运行在pycharm环境,所以采用unittest测试框架来完成测试。

     1. 介绍 unittest测试框架

        unittest单元测试框架是受到 JUnit 的启发,与其他语言中的主流单元测试框架有着相似的风格。其支持测试自动化,配置共享和关机代码测试。支持将测试样例聚合到测试集中,并将测试与报告框架独立。其有五大常见的类:

      (1)TestCase类:测试用例类,最小的完整测试单元,编写用例时需要继承该类来编写测试用例,该类测试方法需要满足如下要求:

           - 测试方法不应该有返回值。

           - 测试方法不应该有任何参数。

           - 测试方法应以test 开头

      (2)TestSuite类:测试套件类,可以将TestCase加入到TestSuite中进行分组,方便对测试进行管理。

      (3)TextTestRunner类:执行测试用例。

      (4)TestResult类:记录测试结果的类。

      (5)TestLoader类:加载测试的类,通常无需手动创建。

     2.  创建unittest文件

      (1)新建一个Python file,命名为:unittest,在kind中进行切换为python unit test的选项,点击OK。

      (2)这样unittest文件就创建好了

    3.3 设计测试用例

       在进行结对编程实验中,我们就对本次实验程序进行了模块划分,分为主函数模块、初始化界面模块、生命游戏规则模块以及绘制动图模块。

     1. 在主函数模块中,设计地图大小用例:N是整数,范围是0<N and N<=100

    用例描述 用例参数 期望结果
    正确用例 N=80 是合法数字
    边界用例 N=100 是合法数字
    错误用例1 N=50.1 是不合法数字,被处理
    错误用例2 N=-80 是不合法数字,被处理
    错误用例3 N=200 是不合法数字,被处理

     2. 在初始化界面模块中,设计初始二维数组用例:N是整型,范围是0<N and N<=100

    用例描述 用例参数 期望结果
    正确用例 N=80 返回80*80的零值数组
    边界用例 N=100 返回100*100的零值数组
    错误用例1 N=50.1 抛出ValueError异常
    错误用例2 N=-80 抛出ValueError异常
    错误用例3 N=200 抛出ValueError异常

      3. 在生命游戏规则模块中,规则为:细胞下一状态由该周围8个细胞的上一生命状态决定。

       (1)周围有三个活细胞则为生(ON);

       (2)周围有两个活细胞,则保持;

       (3)其他情况,则为死(OFF)。

         所以设计生命细胞用例:total是整型,当total<2 or total >3 时 ,细胞生命状态由生(ON)变成死(OFF);当total=3时,生命状态由死(OFF)变成是生(ON)。前提是数组矩阵已经初始化,细胞初始状态已随机生成。

    用例描述 用例参数 期望结果
    用例1 grid[i,j]==ON,total=1; newGrid[i,j]=OFF
    用例2 grid[i,j]==ON,total=4; newGrid[i,j]=OFF
    用例3 grid[i,j]==ON,total=2; newGrid[i,j]=ON
    用例4 grid[i,j]==ON,total=3; newGrid[i,j]=ON
    用例5 grid[i,j]==OFF,total=3; newGrid[i,j]=ON

    用例6

    grid[i,j]==OFF,total=2; newGrid[i,j]=OFF

      4. 在绘制动图模块中,设计绘图动画参数用例,前提是数组矩阵已经初始化,细胞生命状态已随机生成。

    用例描述 用例参数 期望结果
    一般用例 地图能正常使用

    3.4 测试代码

        使用 unittest 模块运行测试用例,一个测试类对应一个被测试功能。

      1. 地图大小Test_Map()

      2. 初始二维数组Test_Init()

      3. 细胞生命状态Test_Life()其中:ON用1替代,OFF用0替代。

     

      4. 绘图动画参数Test_Param()

           因为没有输入,所以测试只进行一般的测试。

    3.5 测试结果与分析

      1. 地图大小Test_Map()

    用例描述 用例参数 实际结果
    正确用例 N=80 测试通过
    边界用例 N=100 测试通过
    错误用例1 N=50.1 测试通过
    错误用例2 N=-80 测试不通过
    错误用例3 N=200 测试不通过

        从结果可以看出,有两个错误用例,但是实际在设计测试用例时,是有三个错误用例,仔细想一想,地图的大小为什么一定要是整型,也可以有不是整型的数据。

      2. 初始二维数组Test_Init()

       测试代码中,等于空不会报错;而不等于空,即是返回了零值数组,即在运行结果中是不通过的,在实际逻辑中,这结果是通过的,逻辑要反过来想。

    用例描述 用例参数 实际结果
    正确用例 N=80 返回80*80的零值数组,测试通过
    边界用例 N=100 返回100*100的零值数组,测试通过
    错误用例1 N=50.1 测试不通过
    错误用例2 N=-80 测试不通过
    错误用例3 N=200 返回100*100的零值数组,测试通过

       分析:

        从结果可以看出,初始二维数组用例结果与地图大小用例的结果不一样的,因为二维数组的大小只能是整型,实验做到这,感觉自己思考问题还是欠佳,以为在地图大小用例中,大小可以是整数也可以是小数,可是这个大小的长度也是二维数组大小的长度。

      3. 细胞生命状态Test_Life()

    用例描述 用例参数 实际结果
    用例1 grid[i,j]==ON,total=1; newGrid[i,j]=OFF
    用例2 grid[i,j]==ON,total=4; newGrid[i,j]=OFF
    用例3 grid[i,j]==ON,total=2; newGrid[i,j]=ON
    用例4 grid[i,j]==ON,total=3; newGrid[i,j]=ON
    用例5 grid[i,j]==OFF,total=3; newGrid[i,j]=O

    用例6

    grid[i,j]==OFF,total=2; newGrid[i,j]=OFF

       分析:从结果来看,实际结果与期望结果是一致的,说明测试模块是正确的。

      4. 绘图动画参数Test_Param()

     

        分析:

            从结果来看,当将动画参数里的"pink" 删除,则会报错,导致程序出现异常而停止运行。

    3.6 提交测试结果到仓库

      将建立的三个unittest文件和测试结果提交到仓库。

     

     

    四、思考题

    比较以下二个工匠的做法,你认为哪种好?结合编码和单元测试,谈谈你的认识。

     答:这两个工匠的做法在顺序上是有不同的,当然在实际工作中,耗费时间也是不同的,工匠一所消耗的时间是多于工匠二的,因为在实际过程中,工匠二的做法是很常见的,因为工匠自己也把握不好砖是否保持水平,如果先拉水平线,每砌一块砖,都与水平线进行比较,这是比较消耗时间的。

           这与编码过程是一样的,是先一边编码一边解决error,还是先编码再进行统一解决error,显然前者比较消耗时间,因为在你编写程序过程中,解决error时,这也可能是个无用功,因为这个产生error的这段代码可能对整个程序是没影响的,然后需要重新写,这又要消耗大量时间的。所以我觉得工匠二的做法比较好。

    五、实验小结

    1. 在运行测试用例时,出现如下错误:AttributeError: 'module' object has no attribute 'TestCase',通过百度发现就是自己新建的python文件名字和内置的函数名冲突,修改了python文件的名字就可以正常运行无报错了。参考链接:https://blog.csdn.net/weixin_38870322/article/details/81395155。

     2. 从大一到现在,写了和看了较多的代码,第一次对代码进行单元测试,通过单元测试,恐怕我以前写的代码大部分会出现许多错误,第一次感受到程序员有责任编写功能代码,同时也就有责任为自己的代码编写单元测试,这让我以后在编码的过程中,不敢太随意了,这就会给进行单元测试的人带了不必要的麻烦。的确,单元测试是由程序员自己来完成,最终受益的也是程序员自己,如果是别人对你的代码进行单元测试,你不知道你的代码哪里出现了问题,然后在下一此的编码过程过程中,你有可能会犯同样的错误,而你自己却不知道,相反你对你自己的代码进行单元测试时,你就会知道自己哪里有问题,同时还收获满满。

     相关参考:

    pycharm创建unittest文件:https://jingyan.baidu.com/article/425e69e60abf74be15fc16a8.html

    解决AttributeError: 'module' object has no attribute 'TestCase'错误:https://blog.csdn.net/weixin_38870322/article/details/81395155。

    参考文档:https://jingyan.baidu.com/article/425e69e60abf74be15fc16a8.html

    参考文档:https://docs.python.org/zh-cn/3/library/unittest.html

  • 相关阅读:
    springboot常见应用属性
    springboot日志
    springboot注解
    2018年5月26日笔记
    LAMP环境搭建与配置
    2018年5月24日笔记
    2018年5月22日笔记
    2018年5月19日笔记
    2018年5月17日笔记
    2018年5月15日笔记
  • 原文地址:https://www.cnblogs.com/1234wh/p/13008038.html
Copyright © 2011-2022 走看看