一、实验目的

  1. 掌握结构化分析与设计方法

  2. 掌握JUnit的用法;

  3. 掌握白盒测试中路径覆盖的测试用例设计;

  4. 掌握黑盒测试中等价类方法;

二、实验内容

1. 某证券公司为了方便提供证券交易服务,欲开发一证券交易平台。该平台接收客户命令信息,可执行以下主要功能:

(1)登录。输入用户名、密码及校验码信息,登录证券交易平台。

(2)开户。如果是新客户,需先提交开户信息,进行开户,并将客户信息存入客户记录中,账户信息(余额等)存入账户记录中;

(3)存款。客户可以向其账户中存款,根据存款金额修改账户余额;

(4)取款。客户可以从其账户中取款,根据取款金额修改账户余额;

(5)证券交易。客户进行证券交易,将交易信息存入交易记录中;

(6)查询交易。平台从交易记录中读取交易信息,将交易明细返回给客户。。

该平台采用结构化分析方法,画出第1层的数据流图如下图所示(有简化):

判断该信息流属于变换流还是事务流,并将该数据流图映射为程序结构,画出程序结构图。

事务流

2. 进行测试驱动的开发

测试驱动开发,英文全称Test-Driven Development,简称TDD,是一种不同于传统软件开发流程的新型的开发方法。它要求在编写某个功能的代码之前先编写测试代码,然后只编写使测试通过的功能代码,通过测试来推动整个开发的进行。这有助于编写简洁可用和高质量的代码,并加速开发过程。

测试驱动开发过程可分为:1)分析需求;2)设计测试用例;3)开发测试代码;4)开发功能代码;5)运行测试,据此修改代码,直到全部通过。

本实验要求按照测试驱动的开发的过程进行一个类的开发。已提供一个类Point2d的源代码。要求编写另一个类Rect,通过左上角点和宽度、高度定义一个长方形(认为它的边分别平行于x和y轴)。要求该类具备:

1)无参构造方法,默认左上角点在(0,0),宽度、高度均为1。

2)单参数构造方法,接收Point2d参数作为左上角点。宽度、高度默认为1。

3)三参数构造方法,接收Point2d参数及两个double值,分别设置其左上角点与宽度、高度。

4)能够计算长方形面积。

5)能够判断某个点是位于长方形内(含在边上)、或其外。

为此,可按照以下步骤进行开发:

1)建立一个空的Rect类;

2)右键点击Rect类,选择NewàJunit Test Case,在对话模式选择:

建立一个测试用例RectCreationTest,用于测试Rect的构造方法。

3)第一个测试可写成:      

@Test
	public void test() {
		Rect r = new Rect();
		assertNotEquals(p, null);
		Point2d p = r.getTopleft();
		assertEquals(r.getWidth(), 1.0, 1.0e-6);
		assertEquals(r.getHeight(), 1.0, 1.0e-6);
	}

即用于测试无参构造方法所得对象,默认左上角点在(0,0),宽度、高度均为1。

4)运行测试,Run AsàJunit Test,不通过。

5)开发功能代码,即写上无参构造方法。

6)再次运行测试,直至通过。至此可保证无参构造方法已编写完毕。

       按照上述过程,将上面要求的其余5项功能按照测试驱动开发的方式编写完成。

Point2d类

package io.shentuzhigang.demo.design.tdo;

/**
 * @author ShenTuZhiGang
 * @version 1.0.0
 * @date 2020-12-24 13:40
 */

public class Point2d {
    private double x;
    private double y;

    public Point2d()
    {
        this(0, 0);
    }

    public Point2d(double x, double y)
    {
        this.x = x;
        this.y = y;
    }

    public double getX() {
        return x;
    }
    public void setX(double x) {
        this.x = x;
    }
    public double getY() {
        return y;
    }
    public void setY(double y) {
        this.y = y;
    }

}

Rect类

package io.shentuzhigang.demo.design.tdo;

/**
 * @author ShenTuZhiGang
 * @version 1.0.0
 * @date 2020-12-24 13:41
 */
public class Rect {
    private Point2d topLeftPoint;
    private int width;
    private int height;
    public Rect() {
        this(new Point2d(0,0),1,1);
    }

    public Rect(Point2d topLeftPoint) {
        this(topLeftPoint,1,1);
    }

    public Rect(Point2d topLeftPoint, int weight, int height) {
        this.topLeftPoint = topLeftPoint;
        this.width = weight;
        this.height = height;
    }

    public double area(){
        return width * height;
    }

    public boolean contain(Point2d point){
        return topLeftPoint.getX()<=point.getX() && point.getX()<=topLeftPoint.getX()+ width
                && topLeftPoint.getY()<=point.getY() && point.getY()<=topLeftPoint.getY()+height;
    }

    public Point2d getTopLeftPoint() {
        return topLeftPoint;
    }

    public void setTopLeftPoint(Point2d topLeftPoint) {
        this.topLeftPoint = topLeftPoint;
    }

    public int getWidth() {
        return width;
    }

    public void setWidth(int width) {
        this.width = width;
    }

    public int getHeight() {
        return height;
    }

    public void setHeight(int height) {
        this.height = height;
    }
}

RectCreationTest类 

package io.shentuzhigang.demo.design.tdo;

import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.*;

/**
 * @author ShenTuZhiGang
 * @version 1.0.0
 * @date 2020-12-24 13:51
 */
public class RectCreationTest {
    @Test
    public void test() {
        Rect r = new Rect();
        assertNotEquals(r, null);
        Point2d p = r.getTopLeftPoint();
        assertEquals(r.getWidth(), 1.0, 1.0e-6);
        assertEquals(r.getHeight(), 1.0, 1.0e-6);
    }

}

 3. 有以下程序(其中的操作并没有什么具体意义,仅用于演示)

基于JUnit,针对以下程序设计测试用例,满足路径覆盖标准。

import java.awt.Point;
public class Path {

	public static Point Coverage(int a, int b, int c)
	{
		Point p = new Point();
		int x = 5;
		int y = 7;
		if(a>b && b>c)
		{
			a += 1;
			x += 6;
			
			if(a==10 || b>20)
			{
				b += 1;
				x = y+4;				
			}
			
			if(a<10 || c==20)
			{
				b += 2;
				y = 4;
			}
			
			a = a + b + 1;
			y = x + y;
		}
		
		if(a>5 || c<10)
		{
			b = c + 5;
			x += 1;
		}
		
		p.x = x;
		p.y = y;
		return p;
	}
}

自行画出程序流图,如教材P329。计算独立路径数量,进而设计基本路径测试所需的测试用例:

序号

测试用例描述

输入参数

期望输出

a

b

c

1

基本路径测试1

 

 

 

 

2

基本路径测试2

 

 

 

 

3

基本路径测试3

 

 

 

 

4

 

 

 

 

 

5

 

 

 

 

 

 

 

 

 

 

 

 

分析:9个区域,8个谓词节点,路径条数为9。

序号

测试用例描述

输入参数

期望输出

a

b

c

1

基本路径测试1

9

8

7

(12,18)

2

基本路径测试2

9

7

8

(6,7)

3

基本路径测试3

10

5

5

(6,7)

4

基本路径测试4

4

3

20

(5,7)

5

基本路径测试5

23

21

20

(12,15)

6

基本路径测试6

10

9

8

(12,18)

7

基本路径测试7

6

8

10

(6,7)

8

基本路径测试8

1

5

9

(6,7)

9

基本路径测试9

1

5

10

(5,7)

package io.shentuzhigang.demo.design;

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

import java.awt.*;

/**
 * @author ShenTuZhiGang
 * @version 1.0.0
 * @date 2020-12-24 19:00
 */
public class PathTest {
    @Test
    public void t(){
        Assertions.assertEquals(Path.Coverage(9,8,7),new Point(12,18));
        Assertions.assertEquals(Path.Coverage(9,7,8),new Point(6,7));
        Assertions.assertEquals(Path.Coverage(10,5,5),new Point(6,7));
        Assertions.assertEquals(Path.Coverage(4,3,20),new Point(5,7));
        Assertions.assertEquals(Path.Coverage(23,21,20),new Point(12,15));
        Assertions.assertEquals(Path.Coverage(10,9,8),new Point(12,18));
        Assertions.assertEquals(Path.Coverage(6,8,10),new Point(6,7));
        Assertions.assertEquals(Path.Coverage(1,5,9),new Point(6,7));
        Assertions.assertEquals(Path.Coverage(1,5,10),new Point(5,7));
    }
}

4. 设计一个静态方法,接收一个字符串,判断该字符串是否为一个格式正确的电话号码。所谓正确格式,有以下规则:

1)如果含3位数值,首位需为1;(110等)

2)如果含8位数值,首位不为0;(本地座机号码)

3)如果含11位数值,首位需为1(手机号码)或0(北京等地座机号码);

4)如果含12位数值,首位需为0;(杭州等地座机号码,第5位不检测)

(国外号码等情况不考虑)

请结合等价类方法给出该方法的测试用例,并基于JUnit,写出测试类。

方法:

1)等价类分为:

  • 有效等价类:是指对于程序的规格说明来说,是合理的,有意义的输入数据构成的集合。

  • 无效等价类:是指对于程序的规格说明来说,是不合理的,无意义的输入数据构成的集合。

2)确立等价类划分原则

原则1:若规定了取值范围,或输入值的个数,则可以确立一个有效等价类和两个无效等价类。

例:程序对输入条件的要求是:…输入数是从1到999…

则  有效等价类是:1≦输入数≦999;

两个无效等价类是:输入数〈1 或 输入数>999

原则2:如果规定了输入数据的一组值,而且程序要对每种输入数据分别处理,则可为每种输入值确立一个有效等价类,此外针对这组值确立一个无效等价类,它是所有不允许的输入值的集合。

例:教师上岗方案中规定对教授、副教授、讲师和助教分别计算分数,做相应的处理。因此可以确定4个有效等价类为教授、副教授、讲师和助教,一个无效等价类,它是所有不符合以上身分的人员的输入值的集合。

原则3:若规定了输入值的集合,或者是规定了“必须如何”的条件,则可确立一个有效等价类和一个无效等价类。

例:某种语言对变量标识符规定必须“以字母打头”,则所有以字母打头的构成有效等价类,而不以字母打头的归于无效等价类。

原则4:如果规定输入数据为整型,则可划分出正整数、零和负整数三个有效类,其他数据为无效类。

原则5:如果程序处理对象是表格,则应使用空表、含一项和多项的表。

3)在确立了等价类之后,建立等价类表,列出所有划分出的等价类。

例如:

4)设计测试用例,覆盖所有的有效等价类组合,和所有的无效等价类组合。

序号

测试用例描述

输入参数

期望输出

覆盖范围

位数

首位

用例

1

有效等价类

3

1

110

true

1,5,8

2

无效等价类

3

2

222

false

2

 

……

 

 

 

 

3

5)Junit测试类:

静态方法

package io.shentuzhigang.demo.design;

import java.util.regex.Pattern;

/**
 * @author ShenTuZhiGang
 * @version 1.0.0
 * @date 2020-12-24 16:24
 */
public class PhoneNumber {
    public static boolean check(String s){
        Pattern pattern = Pattern.compile("^[\\d]*$");
        if(pattern.matcher(s).matches()){
            if(s.length()==3){
                return s.startsWith("1");
            }else if(s.length()==8){
                return s.startsWith("0");
            }else if(s.length()==11){
                return s.startsWith("1") || s.startsWith("0");
            }else if(s.length()==12){
                return s.startsWith("0");
            }
        }
        return false;
    }
}

等价类表

输入等价类

合理等价类

不合理等价类

电话号码

①首位为1的3位数字字符

②首位为0的8位数字字符

③首位为1或0的11位数字字符

④首位为0的12位数字字符

⑤有非数字字符

⑥位数不合法

⑦首位不合法

测试用例

序号

测试用例描述

输入参数

期望输出

覆盖范围

位数

首位

用例

1

有效等价类

3

1

110

true

1,5,7

2

无效等价类

3

2

222

false

1,7

3

有效等价类

8

0

01111111

true

2

4

无效等价类

8

1

11111111

false

2

5

有效等价类

11

1

11111111111

true

3

6

有效等价类

11

0

01111111111

true

3

7

无效等价类

11

1

1111111a110

false

3,5

8

无效等价类

11

0

01111111-110

false

3,5

9

有效等价类

12

0

011111111111

true

4

10

无效等价类

12

1

111111111111

false

4

11

无效等价类

1

1

1

false

6

12

无效等价类

7

1

1111111

false

6

13

无效等价类

8

1

101011a0

false

2,5

Junit测试类

package io.shentuzhigang.demo.design;

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.platform.commons.annotation.Testable;

/**
 * @author ShenTuZhiGang
 * @version 1.0.0
 * @date 2020-12-24 17:06
 */
@Testable
public class PhoneNumberTest{
    @Test
    public void testCheckPhoneNumber1(){
        Assertions.assertTrue(PhoneNumber.check("110"), "电话号码合法");
    }
    @Test
    public void testCheckPhoneNumber2(){
        Assertions.assertFalse(PhoneNumber.check("222"), "电话号码不合法");
    }
    @Test
    public void testCheckPhoneNumber3(){
        Assertions.assertTrue(PhoneNumber.check("01111111"), "电话号码合法");
    }
    @Test
    public void testCheckPhoneNumber4(){
        Assertions.assertFalse(PhoneNumber.check("11111111"), "电话号码不合法");
    }
    @Test
    public void testCheckPhoneNumber5(){
        Assertions.assertTrue(PhoneNumber.check("11111111111"), "电话号码合法");
    }
    @Test
    public void testCheckPhoneNumber6(){
        Assertions.assertTrue(PhoneNumber.check("01111111111"), "电话号码合法");
    }
    @Test
    public void testCheckPhoneNumber7(){
        Assertions.assertFalse(PhoneNumber.check("1111111a110"), "电话号码不合法");
    }
    @Test
    public void testCheckPhoneNumber8(){
        Assertions.assertFalse(PhoneNumber.check("01111111-110"), "电话号码不合法");
    }
    @Test
    public void testCheckPhoneNumber9(){
        Assertions.assertTrue(PhoneNumber.check("011111111111"), "电话号码合法");
    }
    @Test
    public void testCheckPhoneNumber10(){
        Assertions.assertTrue(PhoneNumber.check("011111111111"), "电话号码合法");
    }
    @Test
    public void testCheckPhoneNumber11(){
        Assertions.assertFalse(PhoneNumber.check("1"), "电话号码不合法");
    }
    @Test
    public void testCheckPhoneNumber12(){
        Assertions.assertFalse(PhoneNumber.check("1111111"), "电话号码不合法");
    }
    @Test
    public void testCheckPhoneNumber13(){
        Assertions.assertFalse(PhoneNumber.check("101011a0"), "电话号码不合法");
    }
}

测试结果

参考文章

Junit4单元测试的基本用法

Junit3 单元测试 用等价类测试电话号码

Logo

领路信创诚邀您共建高质量内容社区,投稿申请~

更多推荐