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成功失败以及数据结果,输出到结果报告中

    Unit测试框架包括四个要素:

          (1)测试目标(对象)

      一组认定被测对象或被测程序单元测试成功的预定条件或预期结果的设定。Fixture就是被测试的目标,可以是一个函数、一组对象或一个对象。  测试人员在测试前应了解被测试的对象的功能或行为。

        (2)测试集

    测试集是一组测试用例,这些测试用例要求有相同的测试Fixture,以保证这些测试不会出现管理上的混乱。

        (3)测试执行

    单个单元测试的执行可以按下面的方式进行:

    第一步 编写 setUp() 函数,目的是:建立针对被测试单元的独立测试环境;举个例子,这可能包含创建临时或代理的数据库、目录,再或者启动一个服务器进程。

    第二步 编写所有测试用例的测试体或者测试程序;

    第三步 编写tearDown()函数,目的是:无论测试成功还是失败,都将环境进行清理,以免影响后续的测试;

        (4)断言  

        断言实际上就是验证被测程序在测试中的行为或状态的一个函数或者宏。断言的失败会引发异常,终止测试的执行。

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

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

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

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

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

    三、实验步骤

      1、软件说明:本程序原本使用C++语言编写,打算使用VS2017进行单元测试,但是在创建测试函数使,系统显示打不开“CppUnitTest.h”经过检查和查找资料得知是因为安装目录发生变化导致的,但是修改目录后还是不行,根据现有知识和网上的教程并不能解决此问题,于是决定更改实验代码,使用java语言重新编写生命游戏代码,然后使用Eclipse来进行单元测试。

      2、实验代码:

    LifeGame.java

    package danyuanceshi;

    import java.util.Random;
    import java.util.Scanner;

    /*
    1.生命小游戏的邻居为上下左右和斜对角一共八个位置;
    2.默认选择4*4的格子。
    3.将默认的格子初始化,并打印输出
    4.使用Methods中的方法生成下一轮的状态,并打印
    */

    public class LifeGame {
    public static void main(String[] args) {

    //先设置一个二维数组存储所有的格子
    String[][] lifeMap = new String[4][4];

    Methods me = new Methods();

    //对所有格子进行初始化输入,死为0,活为1
    //随机生成各个位置的邻居情况
    for (int i = 0; i < 4; i++) {
    for (int j = 0; j < 4; j++) {

    int num = new Random().nextInt(2);
    if (num == 1)
    lifeMap[i][j] = "●";
    else if (num == 0)
    lifeMap[i][j] = "○";
    }
    }

    //打印格子初始状态
    System.out.println("初始状态为:");
    me.printLifeMap(lifeMap);
    System.out.println("===========");

    int n = 0;
    int num = 0;//记录变化的次数
    while (n == 0) {

    //用point数组,记录对应位置下一轮的状态,1下一代死,2下一代继续活,3下一代复活
    int[] point = me.check(lifeMap, new int[16]);

    //获得下一次变化后的图形
    lifeMap = me.getNext(lifeMap, point).clone();

    System.out.println("第" + (++num) + "次变化:");

    //打印出来
    me.printLifeMap(lifeMap);
    System.out.println("===========");

    System.out.println("输入0继续进行下一步,输入其他数字退出。");
    n = new Scanner(System.in).nextInt();
    }
    }
    }

    Method.java

    package danyuanceshi;


    public class Methods {

    public Methods() {
    }

    //该方法检测所有位置,并返回对应位置的point数组
    //用point数组,记录对应位置下一轮的状态,1下一代死,2下一代继续活,3下一代复活
    public int[] check(String[][] lifeMap, int[] point) {

    int n = 0;
    //统计周围邻居的情况
    for (int i = 0; i < 4; i++) {
    for (int j = 0; j < 4; j++) {

    /*
    规则如下,进行判断
    1.一个人可以有8个邻居;
    2.一个人若只有一个邻居,在下一代会孤独的死去;
    3.若有2或3个邻居,在下一代依然活着;
    4.若有4个或以上邻居,在下一代会因拥挤而死;
    5.死去的人若有3个邻居,在下一代会复活;
    6.所有的死去或复活都在下一代变化时同时发生。
    */

    //用life变量记录周围活着的邻居个数
    int life = 0;

    //1.判断正下方的位置
    if (i + 1 < 4 && lifeMap[i + 1][j].equals("●")) {
    life++;
    }

    //2.判断右下位置
    if (i + 1 < 4 && j + 1 < 4 && lifeMap[i + 1][j + 1].equals("●")) {
    life++;
    }

    //3.判断左下位置
    if (i + 1 < 4 && j - 1 >= 0 && lifeMap[i + 1][j - 1].equals("●")) {
    life++;
    }

    //4.判断右侧位置
    if (j + 1 < 4 && lifeMap[i][j + 1].equals("●")) {
    life++;
    }

    //5.判断左侧位置
    if (j - 1 >= 0 && lifeMap[i][j - 1].equals("●")) {
    life++;
    }

    //6.判断正上方位置
    if (i - 1 >= 0 && lifeMap[i - 1][j].equals("●")) {
    life++;
    }

    //7.判断右上位置
    if (i - 1 >= 0 && j + 1 < 4 && lifeMap[i - 1][j + 1].equals("●")) {
    life++;
    }

    //8.判断左上位置
    if (i - 1 >= 0 && j - 1 >= 0 && lifeMap[i - 1][j - 1].equals("●")) {
    life++;
    }

    //用一个数组,记录对应位置下一轮的状态,1下一代死,2下一代继续活,3下一代复活
    if (lifeMap[i][j].equals("●")) {
    if (life == 1)
    point[n] = 1;
    else if (life == 2 || life == 3)
    point[n] = 2;
    else if (life >= 4)
    point[n] = 1;
    } else {
    if (life == 3)
    point[n] = 3;
    }
    n++;
    }
    }
    return point;
    }

    public String[][] getNext(String[][] lifeMap, int[] point) {

    int n = 0;
    for (int i = 0; i < 4; i++) {
    for (int j = 0; j < 4; j++) {

    //变更状态
    if (point[n] == 1)
    lifeMap[i][j] = "○";
    if (point[n] == 3)
    lifeMap[i][j] = "●";

    n++;
    }
    }
    return lifeMap;
    }

    public void printLifeMap(String[][] lifeMap){
    for (int i = 0; i < 4; i++) {
    for (int j = 0; j < 4; j++) {
    if (j == 3)
    System.out.println(lifeMap[i][j] + " ");
    else
    System.out.print(lifeMap[i][j] + " ");
    }
    }
    }
    }

     3、创建测试用例

      4、测试用例

       5、编写测试函数

    测试能否正确随机生成地图

     结果显示该用例通过

      6、push测试报告和代码到仓库

     

    思考题

     

    答:我认为第一个工匠的做法好,这样可以及时的发现错误,并修改,资源消耗少,如果等到都弄好了再回头检查就可能需要花费大量的时间去修改。

    实验小结

    虽然实验结束了,但是本人对于测试用例的编写方法还是比较陌生的,对于模块需要怎样写测试用例还是有点生疏,有的模块甚至不知道如何写测试用例。

  • 相关阅读:
    框架集。样式表
    2017.11.23知识点整理
    HTML5的标签
    HTML5大体概括截图
    2017.11.21 通用标签及属性
    2017.11.21 课程随记
    JavaScript数组
    JavaScript语句
    javascript基础知识
    不用alert提示的非空表单验证
  • 原文地址:https://www.cnblogs.com/jsjqkx/p/13020647.html
Copyright © 2011-2022 走看看