计算器软件测试
江西工业职业技术学院
毕业综合实践
课题名称: 安卓手机计算器
作 者: 廖杰 学 号: 20112715 分 院: 电子与信息
分院 专 业: 计算机网络技术
指导老师: 占华为 专业技术职务 讲师
2013年 11 月 14日
一、问题描述:
用Java实现求两整数的加、减、乘、除运算结果,
两整数的范围都是[0,100]。从键盘输入数m,数n,判断他们的范围,若不在[0,100]范围内,则输出提示语,要求重新输入,并且在做除法运算时,当除数为0时,输出提示语,说明除数不能为0。
将两数的范围定在[0,100],以更好的进行边界值分析,等价类测试等黑盒测试方法;为充分体现白盒测试的特点,有些地方故意用了if-else语句,while循环;另外,加、减、乘、除运算分别用了四个函数来计算,这样既可以在主函数中调用,又可以在Junit框架中分别对这四种运算进行测试。
二、程序源代码:
1. import java.util.Scanner;
2. public class Computer {
3. private int a;
4. private int b;
5. public Computer (int x,int y) //构造函数,初始化 6. {
7. a=x; //注意:Juint中需要输入测试值在0~100范围内 8. b=y;
9. }
10. public double add() //加法
11. {
12. return a+b;
13. }
14. public double minus() //减法
15. {
16. return a-b;
17. }
18. public double multiply() //乘法
19. {
20. return a*b;
21. }
22. public double divide() //除法,要求除数不能为0,否则输出提示语 23. {
24. if(b!=0)
25. return a/b;
26. else
27. System.out.println("除数不能为0~");
28. return 0;
29. }
30. public static void main(String[] args) 31. {
32. Scanner scanner = null;
33. scanner = new Scanner(System.in);
34. System.out.println("请输入0~100的两个m,n整数:"); 35. System.out.println("请输入第一个数:");
36. while(true){ //若输入值不在要求范围内,则有循环直到输入正确为止 37. int m = scanner.nextInt(); 38. if(m>=0&&m<=100)
39. {
40. System.out.println("请输入第二个数:"); 41. while(true){
42. int n = scanner.nextInt(); 43. if(n>=0&&n<=100)
44. {
45. Computer cpt=new Computer(m,n); 46. System.out.println("运算结果是:");
47. System.out.println("加法:"+m+"+"+n+"="+cpt.add()); 48. System.out.println("减法:"+m+"-"+n+"="+cpt.minus()); 49. System.out.println("乘法:"+m+"*"+n+"="+cpt.multiply(); 50. System.out.println("除法:"+m+"/"+n+"="+cpt.divide()); 51. }
52. else
53. System.out.print("输入n值不在要求区间,请重新输入n:\n"); 54. }
55. }
56. else
57. System.out.print("输入m值不在要求区间,请重新输入m:\n"); 58. }
59. }
60. }
三、黑盒测试:
1、边界值测试:
1.1、边界值分析:
输入要求是0~100之间的整数,因此产生了0和100两个边界,边界值分析可产生4*2+1=9个测试用例。可构造(50,0)、(50,1)、(50,50)、(50,99)、(50,100)、(0,50)、(1,50)、(99,50)、(100,50) 这9组测试用例。
1 边界值分析测试用例
m n 用例 预期输出(+、-、*、/) 实际输出(+、-、*、/)
1 50 0 50.0、50.0、0.0、除数不为0~ 50.0、50.0、0.0、0.0
2 50 1 51.0、49.0、50.0、50.0 51.0、49.0、50.0、50.0
3 50 50 100.0、0.0、2500.0、1.0 100.0、0.0、2500.0、1.0
4 50 99 149.0、-49.0、4950.0、0.505 109.0、-9.0、2950.0、
0.5050505050505051
5 50 100 150.0、-50.0、5000.0、0.5 150.0、-50.0、5000.0、0.5
6 0 50 50.0、-50.0、0.0、0.0 50.0、-50.0、0.0、0.0
7 1 50 51.0、-49.0、50.0、0.02 51.0、-49.0、50.0、0.02
8 99 50 149.0、49.0、4950.0、1.98 149.0、49.0、4950.0、1.98
9 100 50 150.0、50.0、500.0、2.0 150.0、50.0、500.0、2.0
在该测试时,发现(50,0)这个测试的除法的预期输出和实际输出不一致,是因为代码中return 0;当除数n=0时,实际返回的是0.0。
public double divide()
{
if(b!=0)
return a/b;
else
System.out.println("除数不能为0~");
return 0; //出现Bug
}
1.2健壮性测试:
健壮性测试可产生6*2+1=13个测试用例,在上面边界值分析测试用例的基础上再添加(50,-1)、(50,101)、(-1,50)、(101,50)这4个测试用例即可。
表2 健壮性测试用例
m n 用例 预期输出(+、—、*、/) 实际输出
10 50 -1 49.0、51.0、-50.0、-50.0 输入n值不在要求区间
11 50 101 151.0、-51.0、5050.0、0.495 输入n值不在要求区间
12 -1 50 49.0、-51.0、-50.0、-0.02 输入m值不在要求区间
13 101 50 151.0、51.0、5050.0、2.02 输入m值不在要求区间
2、等价类测试:
规定了输入值m,n的范围[0,100],则
变量的有效等价类:
M1={m:0?m?100}
N1={n:0?n?100}
变量的无效等价类:
M2={m:m?0}
M3={m:m?100}
N2={n:n?0}
N3={n:n?0}
2.1、弱一般等价类测试:
该测试可用每个等价类的一个变量实现,可生成1个测试用例。
2.2、强一般等价类测试:
该测试基于多缺陷假设,需要1*1=1个测试用例。
表3 强、弱一般等价类测试用例
m n 用例 预期输出(+、—、*、/) 实际输出
50 50 WN1,SN1 100.0、0.0、2500.0、1.0 100.0、0.0、2500.0、1.0
2.3、弱健壮等价类测试:
该测试基于单缺陷假设,不仅对有效值测试,还考虑了无效值,可生成1+2+2=5个测试用例。
表 4 弱健壮等价类测试用例
m n 用例 预期输出(+、—、*、/) 实际输出
50 50 WR1 100.0、0.0、2500.0、1.0 100.0、0.0、2500.0、1.0
50 -1 WR2 49.0、51.0、-50.0、-50.0 输入n不在要求区间
50 101 WR3 151.0、-51.0、5050.0、0.495 输入n不在要求区间
-1 50 WR4 49.0、-51.0、-50.0、-0.02 输入m不在要求区间
101 50 WR5 151.0、51.0、5050.0、2.02 输入m不在要求区间
2.4、强健壮等价类测试:
该测试基于多缺陷假设,从所有等价类笛卡尔积的每个元素中获得测试用例,可生成(1+2)*(1+2)=9个测试用例。
表5 弱健壮等价类测试用例
m n 用例 预期输出(+、—、*、/) 实际输出
-1 -1 SR1 -2.0、0.0、1.0、1.0 输入m值不在要求区间
-1 50 SR2 49.0、-51.0、-50.0、-0.02 输入m值不在要求区间
-1 101 SR3 100.0、-102.0、-101.0、0099 输入m值不在要求区间
50 -1 SR4 49.0、51.0、-50.0、-50.0 输入n值不在要求区间
50 50 SR5 100.0、0.0、2500.0、1.0 100.0、0.0、2500.0、1.0
50 101 SR6 106.0、-51.0、5050.0、0.495 输入m值不在要求区间
-1 101 SR7 100.0、102.0、-101.0、-101.0 输入m值不在要求区间
50 101 SR8 151.0、51.0、5050.0、20.2 输入m值不在要求区间
101 101 SR9 202.0、0.0、10201.0、1.0 输入m值不在要求区间
3、因果图:
C1:输入m值在[0,100]内 C2:输入n值在[0,100]内
e1:输出结果 e2:输入m值不在区间 e3:输入n值不在区间
e1 m C1
e2
„ C2 C1
e3
3.基于决策表的测试:
变量的有效等价类:
M1={m:0?m?100}
N1={n:0?n?100}
变量的无效等价类:
M2={m:m?0}
M3={m:m?100}
N2={n:n?0}
N3={n:n?0}
表6 基于决策表的测试用例
1 2 3 4 桩
M1 M1 C1:第一个数在: M2,M3 M2,M3
N1 N1 C2:第二个数在: N2,N3 N2,N3
X A1:正常输出结果:
X X A2:输入m值不在要求区间,重新输入
X X A3:输入n值不在要求区间,重新输入
二、白盒测试:
1.测试覆盖指标:
流程图:
入口
a
执行语句块1 f
执行语句块5
F T m>=0&&m<=100
b
d
执行语句块2 F
执行语句块4
F T n>=0&&n<=100
c
执行语句块3
出口
1.1、语句覆盖:
每个可执行的语句必须至少执行一次,则测试用例:
表 7 语句覆盖测试用例
测试用例 通过路径 条件取值
a,f,b,d,c F1,T1,F2,T2 m1=-1,m2=1;n1=101,n2=99
1.2、(判定)分支覆盖:
每个判定必须至少获得一次“真”值和“假”值,则测试用例:
表 8(判定)分支覆盖
测试用例 通过路径 条件取值 m=1,n=99 a,b,c T;T m1=-1,m2=1,n=99 a,f,b,c F1,T1;T2 m=1,n1=-1,n2=99 a,b,d,c T;F1,T1 m1=-1,m2=1;n1=101,n2=99 a,f,b,d,c F1,T1;F2,T2
1.3、条件判定覆盖:
每个判定中的每个条件的所有可能值(真/假)至少出现一次并且每个判定本身的结果(真/假)也至少出现一次,则测试用例:
表 9 条件判定覆盖
测试用例 通过路径 条件取值 m=1,n=99 a,b,c T,T
1.4、路径覆盖:
测试用例要求覆盖所有可能的路径:
表10 路径覆盖
测试用例 通过路径 条件取值
a,f,b,d,c F1,T1,F2,T2 m1=-1,m2=1;n1=101,n2=99
2.基路径测试:(为简便画图,开始点从主要代码第36行开始,其他无分支节点用省略号表示)
1
(从节点1到节点36) ...
36
37
38 56
39
40
57
41
42
43 52
(从节点43到51) ...
53 51
54
55
58
59
所以:圈复杂度V(G)= 5 60
4个独立路径为:
P1:1,...,36,37,38...42,43...54,55,58,59,60
P2:1,...,36,37,56,57,58,59,60
P3:1,...,36,37,38...42,52,53,54,55,58,59,60
P4:1,...,36,37,38...42,43...54,42,52,53,54,55,58,59,60
P5:1,...,36,37,56,57,58,36,37,56,57,58,59,60
三、Junit动态测试:
import junit.framework.TestCase; //引入Junit框架中所有类 public class ComputerTest extends TestCase {
private Computer a; //定义变量
private Computer b;
private Computer c;
private Computer d;
public ComputerTest (String name) {
super(name); //构造函数,使用super关键字直接引用父类TestCase的构造函数
}
protected void setUp() throws Exception {
super.setUp(); //建立环境,可以进行一些测试数据的初始化,还可以把数据库联接写在此处,以减少重复性工作,提高效率
a = new Computer(1,99);
b = new Computer(101,5);
c = new Computer(1,2);
d = new Computer(1,0);
}
protected void tearDown() throws Exception {
super.tearDown();
}
public void testComputer() {
int x=1;
int y=1;
assertSame(x,y); //assertSame()
}
public void testAdd() { //加法测试
assertEquals(100.0, a.add());
}
public void testAdd1() {
assertEquals(106.0, b.add()); //输入值超出要求范围 ,测试失败,因为要求0~100范围,所以,在构造Computer()函数时,要规定变量x,y的范围
}
public void testMinus() { //减法测试
assertEquals(-89,a.minus()); //预期值和结果不相等,测试失败
}
public void testMinus1() {
assertEquals(-89.0,a.minus());
}
public void testMultiply() {
assertEquals(2.0,c.multiply());
}
public void testMultiply1() {
Object tx = null;
try{
assertEquals(2.0,c.multiply()); //c.multiply()的正常结果是2.0,所以在此不应抛出异常
fail("should have thrown an exception"); //上面不抛出异常,不执行fail()
}catch (Exception e) {
tx = e;
}
assertNotNull(tx); //断言tx不为空,则一定有异常
}
public void testDivide() {
assertEquals(0.0,d.divide()); //因divide()函数中,当d=0时,返回0.0,所以这里测试应该成功,虽然希望得到“被除数不能为0~”的情况
}
public void testDivide1() {
assertEquals(0.5, c.divide());
}
public void testEmpty(){
try{
c.divide();
fail("should have thrown an exception ");
}catch(RuntimeException Ex){
assertTrue(true); //assertTrue(true);
}
}
}
截图:
(1)、测试testAdd1()时,由于b=new
Computer(101,5);assertEquals(106.0, b.add());输入值范围超出定义范围,测试异常;
(2)、测试testMinus()时,assertEquals(-89,a.minus());预期值和实际值不相同,测试异常;
(3)、测试testMultiply()时,断言不为空,抛出异常
Junit集成测试:
在Junit中,Test Case总是对所有方法进行测试,而Test Suite中可对其中的一部分方法测试,而且一个测试类中还可以包含其他测试类。在Test Case类中声明一个public static Test suite()方法即可完成多个测试类的集成。
例如,在上例ComputerTest类中,添加:
public static Test Suit()
{
TestSuite suite=new TestSuite();
suite.addTest(new ComputerTest("testAdd"));
suite.addTest(new ComputerTest("testMultiply1"));
return suite;
}
若在另一个ComputerTest1类中,要测试ComputerTest中的所有测试方法,则应:
public class ComputerTest1 extends TestCase
{
public ComputerTest1(String name)
{
Super(name);
}
public static Test Suit()
{
TestSuite suite=new TestSuite();
suite.addTestSuite(ComputerTest.class);
return suite;
}
}
四、测试用例
体会:
在这次软件测试过程中,我扮演了用户、程序员、测试员三钟角色,为了充分体现黑盒、白盒以及Junit动态测试的特点,我特意设计了一个[0,100]之间的整数简单加减乘除运算。
对于黑盒测试,在设计测试用例时完全没有考虑程序内部的逻辑结构和内部特性,只纯粹的依据功能和要求来设计测试用例进行测试。测试时使用了多种测试方法,包括边界值测试(边界值分析、健壮性测试、特殊值测试),等价类测试(弱一般、强一般、弱健壮、强健壮等价类测试),基于决策表的测试等。虽然黑盒测试很容易生成测试用例,但实际上只有一小部分可能的输入被测试到,某些代码得不到测试,不能直接对隐蔽了许多问题的特定程序段进行测试,不易进行充分性测试。比如:在上例中,若输入的数据是2,0,则输出的是0.0 ,很明显这个结果是不正确的。
对于白盒测试,在设计测试用例时首先对程序进行分析,从程序的内部结构出发设计测试用例,涉及到程序的控制方法、源语句、编码细节、数据库设计等。设计测试用例时应用了白盒测试中的多种测试方法,其中包括:测试覆盖(语句覆盖、分支覆盖、分支条件覆盖等)、基路径测试等方法。白盒测试中,对代码的测试比较透彻,但不容易生成测试用例,而且测试工作量很大,。因为白盒测试是基于代码的基础上,所以对变量的定义、数据的分析、精度的度量都有严格的要求。如:上例中要求输入的值是[0,100]之间的int型,而输出的结果却要求的是double型。
对于Junit,编写了另一段代码来测试要执行的代码,代码中的一个测试类(TestCase)包含了很多测试方法(testXXXX),每个测试方法中又有很多测试断言(assertXXXX),在方法中测试预期值是否和实际值一致。
总之,在这次测试设计让我对软件测试有了一个深入了解,对于测试方法、测试过程,都有了较好的认识,学会了如何进行黑盒测试、白盒测试、以及一些测试工具(如Junit)。当然,对于以后企业上的软件测试,还有待很大的提高。