实验五:单元测试

一、实验目的

1)掌握单元测试的方法

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

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

二、实验内容与要求

1、了解单元测试的原理与框架

2、对“结对编程”实验的程序模块(类)进行单元测试,提交单元测试报告

(1)程序源码

  1 package lifegame;
  2 
  3 public class Methods {
  4      public Methods() {
  5         }
  6 
  7         //该方法检测所有位置,并返回对应位置的point数组
  8         //用point数组,记录对应位置下一轮的状态,1下一代死,2下一代继续活,3下一代复活
  9         public int[] check(String[][] lifeMap, int[] point) {
 10 
 11             int n = 0;
 12             //统计周围邻居的情况
 13             for (int i = 0; i < 4; i++) {
 14                 for (int j = 0; j < 4; j++) {
 15 
 16                     /*
 17                     规则如下,进行判断
 18                                       ( 1)一个人可以有8个邻居;
 19                                       ( 2)一个人若只有一个邻居,在下一代会孤独的死去;
 20                             (3)若有2或3个邻居,在下一代依然活着;
 21                             (4)若有4个或以上邻居,在下一代会因拥挤而死;
 22                             (5)死去的人若有3个邻居,在下一代会复活;
 23                             (6)所有的死去或复活都在下一代变化时同时发生。
 24                     */
 25 
 26                     //用life变量记录周围活着的邻居个数
 27                     int life = 0;
 28 
 29                     //1.判断正下方的位置
 30                     if (i + 1 < 4 && lifeMap[i + 1][j].equals("●")) {
 31                         life++;
 32                     }
 33 
 34                     //2.判断右下位置
 35                     if (i + 1 < 4 && j + 1 < 4 && lifeMap[i + 1][j + 1].equals("●")) {
 36                         life++;
 37                     }
 38 
 39                     //3.判断左下位置
 40                     if (i + 1 < 4 && j - 1 >= 0 && lifeMap[i + 1][j - 1].equals("●")) {
 41                         life++;
 42                     }
 43 
 44                     //4.判断右侧位置
 45                     if (j + 1 < 4 && lifeMap[i][j + 1].equals("●")) {
 46                         life++;
 47                     }
 48 
 49                     //5.判断左侧位置
 50                     if (j - 1 >= 0 && lifeMap[i][j - 1].equals("●")) {
 51                         life++;
 52                     }
 53 
 54                     //6.判断正上方位置
 55                     if (i - 1 >= 0 && lifeMap[i - 1][j].equals("●")) {
 56                         life++;
 57                     }
 58 
 59                     //7.判断右上位置
 60                     if (i - 1 >= 0 && j + 1 < 4 && lifeMap[i - 1][j + 1].equals("●")) {
 61                         life++;
 62                     }
 63 
 64                     //8.判断左上位置
 65                     if (i - 1 >= 0 && j - 1 >= 0 && lifeMap[i - 1][j - 1].equals("●")) {
 66                         life++;
 67                     }
 68 
 69                     //用一个数组,记录对应位置下一轮的状态,1下一代死,2下一代继续活,3下一代复活
 70                     if (lifeMap[i][j].equals("●")) {
 71                         if (life == 1)
 72                             point[n] = 1;
 73                         else if (life == 2 || life == 3)
 74                             point[n] = 2;
 75                         else if (life >= 4)
 76                             point[n] = 1;
 77                     } else {
 78                         if (life == 3)
 79                             point[n] = 3;
 80                     }
 81                     n++;
 82                 }
 83             }
 84             return point;
 85         }
 86 
 87         public String[][] getNext(String[][] lifeMap, int[] point) {
 88 
 89             int n = 0;
 90             for (int i = 0; i < 4; i++) {
 91                 for (int j = 0; j < 4; j++) {
 92 
 93                     //变更状态
 94                     if (point[n] == 1)
 95                         lifeMap[i][j] = "○";
 96                     if (point[n] == 3)
 97                         lifeMap[i][j] = "●";
 98 
 99                     n++;
100                 }
101             }
102             return lifeMap;
103         }
104 
105         public void printLifeMap(String[][] lifeMap){
106             for (int i = 0; i < 4; i++) {
107                 for (int j = 0; j < 4; j++) {
108                     if (j == 3)
109                         System.out.println(lifeMap[i][j] + " ");
110                     else
111                         System.out.print(lifeMap[i][j] + " ");
112                 }
113             }
114         }
115 }
Methods.java
 1 package lifegame;
 2 
 3 import java.util.Random;
 4 import java.util.Scanner;
 5 
 6     /*
 7         (1)生命小游戏的邻居为上下左右和斜对角一共八个位置
 8         (2)默认选择4*4的格子
 9         (3)将默认的格子初始化,并打印输出
10         (4)使用Methods中的方法生成下一轮的状态,并打印
11      */
12 
13 public class LifeGame {
14     public static void main(String[] args) {
15 
16             //设置一个二维数组存储所有的格子
17             String[][] lifeMap = new String[4][4];
18 
19             Methods me = new Methods();
20 
21             //将所有格子进行初始化输入,死为0,活为1
22             //随机生成各个位置的邻居情况
23             for (int i = 0; i < 4; i++) {
24                 for (int j = 0; j < 4; j++) {
25 
26                     int num = new Random().nextInt(2);
27                     if (num == 1)
28                         lifeMap[i][j] = "●";
29                     else if (num == 0)
30                         lifeMap[i][j] = "○";
31                 }
32             }
33 
34             //打印格子初始状态
35             System.out.println("初始状态为:");
36             me.printLifeMap(lifeMap);
37             System.out.println("===========");
38             
39             Scanner scan = new Scanner(System.in);
40             int n = 0;
41             int num = 0;//记录变化的次数
42             while (n == 0) {
43 
44                 //用point数组,记录对应位置下一轮的状态,1下一代死,2下一代继续活,3下一代复活
45                 int[] point = me.check(lifeMap, new int[16]);
46 
47                 //将获得下一次变化后的图形
48                 lifeMap = me.getNext(lifeMap, point).clone();
49 
50                 System.out.println("第" + (++num) + "次变化:");
51 
52                 //打印出来
53                 me.printLifeMap(lifeMap);
54                 System.out.println("===========");
55 
56                 System.out.println("输入0继续进行下一步,输入其他数字退出。");
57                 if(scan.hasNextInt()) {
58                     n = scan.nextInt();
59                 }
60                 else n = 1;
61             }
62             scan.close();
63         }
64 }
LifeGame.java

 

(2)测试用例设计

check方法测试用例:

输入参数 期望结果

{{"●","●","○","○"},

 {"○","●","○","○"},

 {"○","○","○","○"},

 {"●","○","○","●"}}

{2,2,0,0,3,2,0,0,0,0,0,0,1,0,0,1}

 

 

 

 

 

 

getNext方法测试用例:

输入参数 期望结果

{{"●","●","○","○"},

 {"○","●","○","○"},

 {"○","○","○","○"},

 {"●","○","○","●"}}

{2,2,0,0,3,2,0,0,0,0,0,0,1,0,0,1}

{{"●","●","○","○"},

 {"●","●","○","○"},

 {"○","○","○","○"},

 {"○","○","○","○"}}

 

 

 

 

 

 

 

(3)框架选择与安装过程

由于源程序是java程序,所以这里选择JUnit4作为测试框架。

JUnit是一个Java语言的单元测试框架。它由Kent Beck和Erich Gamma建立,逐渐成为源于Kent Beck的sUnit的xUnit家族中最为成功的一个。 JUnit有它自己的JUnit扩展生态圈。多数Java的开发环境都已经集成了JUnit作为单元测试的工具。

安装过程:

进入eclipse

实验五:单元测试_第1张图片

 

 实验五:单元测试_第2张图片

 

 实验五:单元测试_第3张图片

 

 实验五:单元测试_第4张图片

 

 实验五:单元测试_第5张图片

 

 实验五:单元测试_第6张图片

 

 实验五:单元测试_第7张图片

 

 实验五:单元测试_第8张图片

 

 实验五:单元测试_第9张图片

 

 实验五:单元测试_第10张图片

 

(4)测试代码

 1 package lifegame;
 2 
 3 import static org.junit.Assert.*;
 4 
 5 import java.lang.reflect.Array;
 6 import java.util.ArrayList;
 7 import java.util.Arrays;
 8 
 9 import org.junit.Before;
10 import org.junit.Test;
11 
12 public class MethodsTest {
13 
14     @Before
15     public void setUp() throws Exception {
16     }
17 
18     @Test
19     public void testCheck() {
20         Methods me = new Methods();
21         String[][] teststr = {{"●","●","○","○"},{"○","●","○","○"},{"○","○","○","○"},{"●","○","○","●"}};
22         int[] exp_result = {2,2,0,0,3,2,0,0,0,0,0,0,1,0,0,1};
23         assertEquals(true, Arrays.equals(exp_result, me.check(teststr)));
24     }
25 
26     @Test
27     public void testGetNext() {
28         Methods me = new Methods();
29         String[][] teststr2 = {{"●","●","○","○"},{"○","●","○","○"},{"○","○","○","○"},{"●","○","○","●"}};
30         int[] point = {2,2,0,0,3,2,0,0,0,0,0,0,1,0,0,1};
31         String[][] exp_str = {{"●","●","○","○"},{"●","●","○","○"},{"○","○","○","○"},{"○","○","○","○"}};
32         me.getNext(teststr2, point);
33         int c = 0;
34         for(int m = 0; m < 4; m++) {
35             for(int n = 0; n < 4; n++) {
36                 if(exp_str[m][n].equals(teststr2[m][n])) c++;
37             }
38         } //判断生成串是否与期望串相等
39         assertEquals(16, c);
40     }
41 }
Methods.java

(5)测试结果与分析

测试结果如图:

实验五:单元测试_第11张图片

 

经测试 check方法无法得到期望返回。对check方法代码段进行分析:

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

 发现未指明当life==0时的情况(所找到的游戏规则不严谨)

修改后:

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

再运行单元测试:

实验五:单元测试_第12张图片

 

 通过测试!!!

 

3、push测试报告和测试代码到各自的github仓库

 实验五:单元测试_第13张图片

 

 

三、思考题:

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

实验五:单元测试_第14张图片

我觉得工匠一的做法比较好,在初始阶段就做好纠错的工作,避免成型之后进行纠错的困难与极大开销。

编码初期就应该时刻注意代码的整体构思,确定好类或函数的接口参数,尽可能的避免不必要的错误。

 

实验小结

本次实验是对单元测试的考察,由于我们的程序基本都属于字符串的生成与检查,再进行单元测试时,assertEquals()方法不能直接使用,较为麻烦。

更令人意想不到的是check方法没有通过测试,虽然后来找出了原因,但这也让我对单元测试的重要性有了进一步的认识。该方法中存在的错误虽然不会导致程序无法运行,但也是很大的隐患(原本该置“1”的元素没有执行),如果不是在单元测试中发现了问题,可能这个Bug会存在很久。

 

你可能感兴趣的:(实验五:单元测试)