51Testing软件测试论坛

标题: Junit4 单元测试框架的常用方法介绍 [打印本页]

作者: 海鸥一飞    时间: 2018-3-7 16:14
标题: Junit4 单元测试框架的常用方法介绍
Junit 介绍:

Junit是一套框架(用于JAVA语言),由 Erich Gamma 和 Kent Beck 编写的一个回归测试框架(regres
sion testing framework),即用于白盒测试。



本文介绍的内容:

1 Junit Myeclipse测试类的生成

2 JUnit 4 常用的几个annotation 介绍与使用 (@Before  @After @Test @Ignore @BeforeClass @AfterClass)

3 常用的断言介绍

4 特殊的处理 (限时测试,异常测试)

5 参数化配置 (@Parameters)



准备测试代码

测试的方法(正确、错误方法、死循环、抛异常)

LogicService
  1. package com.logic;

  2. public class LogicService {
  3.    
  4.     public int add(int x ,int y){ //加法
  5.         return x+y;
  6.     }
  7.    
  8.     public int sub(int x ,int y){ //减法
  9.         return x-y;
  10.     }
  11.    
  12.     public int div(int x ,int y){ //除法
  13.         return x/y;
  14.     }
  15.    
  16.     public int div2(int x ,int y){ //除法  做了异常判断
  17.         try {
  18.             int z = x/y;
  19.         } catch (Exception e) {
  20.             e.printStackTrace();
  21.         }
  22.         return x/y;
  23.     }
  24.    
  25.     public void loop(int x ,int y){ //死循环
  26.         for(;;)
  27.             x=y;
  28.     }   
  29.    
  30.     public void unCompleted(int x ,int y){ //未完成的模块
  31.         //还在开发中
  32.     }
  33.    
  34. }
复制代码
一  Myeclipse测试类的生成

1 对需要测试的类点右键 NEW 一个Junit Test Case

2 点击NEXT

注意 1 选择NEW Junit 4 test

        2 source folder 是默认会填写上之前右键NEW的那个类,如果不是的话,请自行进行修改

        3 package 默认会填写当前的包名 个人建议重新开个测试包-即在包后面加上.test 表示是单元测
试用例专用包与源代码分离

        4 name 默认会在之前右键NEW的那个类的基础的后面加上Test 如果不是的话,建议自行进行
修改,便于标示

        5 初始化的方法,我一般会勾上个setUp,这个请随意。

3 继续点击NEXT

1 这里可以选择需要测试的方法,我一般都是全选的然后在生成的测试类中再做处理

2 点击finish完成

  1. 4 生成的单元测试类

  2. 复制代码
  3. package com.logic.test;

  4. import static org.junit.Assert.*;

  5. import org.junit.Before;
  6. import org.junit.Test;

  7. public class LogicServiceTest {

  8.     @Before
  9.     public void setUp() throws Exception {
  10.     }

  11.     @Test
  12.     public void testAdd() {
  13.         fail("Not yet implemented");
  14.     }

  15.     @Test
  16.     public void testSub() {
  17.         fail("Not yet implemented");
  18.     }

  19.     @Test
  20.     public void testDiv() {
  21.         fail("Not yet implemented");
  22.     }

  23.     @Test
  24.     public void testDiv2() {
  25.         fail("Not yet implemented");
  26.     }

  27.     @Test
  28.     public void testLoop() {
  29.         fail("Not yet implemented");
  30.     }

  31.     @Test
  32.     public void testUnCompleted() {
  33.         fail("Not yet implemented");
  34.     }

  35. }
复制代码
二 JUnit 4 常用的几个annotation 介绍与使用 (@Before  @After @Test @Ignore @BeforeClass @After
Class)

1 常用的annotation介绍

@Before:初始化方法,在任何一个测试执行之前必须执行的代码;

@After:释放资源,在任何测试执行之后需要进行的收尾工作。在每个测试方法执行之后执行一次,
该annotation只能修饰public void 方法;

@Test:测试方法,表明这是一个测试方法。在Junit中将会自动被执行。该annotation只你呢个修饰p
ublic void 方法。

@Ignore:忽略的测试方法,标注的含义就是“某些方法尚未完成,暂不参与此次测试”;

@BeforeClass:针对所有测试,只执行一次,且必须为public static void;一般用于初始化必要的消耗
较大的资源,例如数据库连接等

@AfterClass:针对所有测试,将会在所有测试方法执行结束后执行一次,且必须为public static void;



2 常用的annotation测试

修改单元测试类LogicServiceTest
  1. package com.logic.test;

  2. import static org.junit.Assert.*;

  3. import org.junit.After;
  4. import org.junit.AfterClass;
  5. import org.junit.Before;
  6. import org.junit.BeforeClass;
  7. import org.junit.Ignore;
  8. import org.junit.Test;

  9. public class LogicServiceTest {

  10.     @Before
  11.     public void setUp() throws Exception {
  12.         System.out.println("@Before");//测试@Before
  13.     }

  14.     @After
  15.     public void end() throws Exception {
  16.         System.out.println("@After");//测试@@After
  17.     }

  18.     @BeforeClass
  19.     public static void init() throws Exception {
  20.         System.out.println("@BeforeClass");//测试@BeforeClass
  21.     }

  22.     @AfterClass
  23.     public static void disstroy() throws Exception {
  24.         System.out.println("@AfterClass");//测试@AfterClass
  25.     }

  26.     @Test
  27.     public void testAdd() {
  28.         System.out.println("@Test testAdd");//测试@Test
  29.     }

  30.     @Test
  31.     public void testSub() {
  32.         System.out.println("@Test testSub");//测试@Test
  33.     }

  34.     @Ignore
  35.     public void testDiv() {
  36.         System.out.println("@Ignore ");//测试@Ignore
  37.     }

  38.     @Ignore
  39.     public void testDiv2() {
  40.         System.out.println("@Ignore ");//测试@Ignore
  41.     }

  42.     @Ignore
  43.     public void testLoop() {
  44.         System.out.println("@Ignore ");//测试@Ignore
  45.     }

  46.    
  47.     public void testUnCompleted() {
  48.         System.out.println("@Ignore ");//测试未标注
  49.     }

  50. }
复制代码
执行结果


执行结果分析

1 @BeforeClass和@AfterClass只执行一次,在所有方法开始前/后

2 @Before和@After在每个@Test标注的方法前后各执行一次

3 @Test 标注的方法都会执行一次(表示要测试的方法)

4 @Ignore方法不会被执行,没有annotation的方法也不会被执行

5 总结  @BeforeClass –> (@Before –> @Test –> @After) –> @AfterClass



理解了以上注解后就可以尝试着在日常的项目中使用Junit进行单元测试了。



3 常用的断言介绍

assertEquals(String msg, Object expectRes, Object Res) --------  用于值判断

判断expectRes.equals(Res) ,表示值等于的判断,失败则抛MSG

assertSame(String msg, Object expectRes, Object Res)  --------  用于地址判断

判断expectRes==Res,表示地址等于的判断,失败则抛MSG

assertTrue(String msg,Boolean result) ----------------------------用于Boolean判断

判断result是true还是false,失败则抛MSG

assertNull(String msg,Object result)-------------------------------用于NULL判断

判断result是否为NULL,失败则抛MSG

fail(String msg);---------------------------------------------------直接中止方法运行

直接中止方法运行,抛出MSG



测试代码
  1. package com.logic.test;

  2. import static org.junit.Assert.*;

  3. import org.junit.After;
  4. import org.junit.AfterClass;
  5. import org.junit.Before;
  6. import org.junit.BeforeClass;
  7. import org.junit.Ignore;
  8. import org.junit.Test;

  9. import com.logic.LogicService;

  10. public class LogicServiceTest {
  11.     LogicService logserv ;

  12.     @Before
  13.     public void setUp() throws Exception {
  14.         logserv = new LogicService();
  15.     }

  16.     @Test
  17.     public void testAdd() {
  18.         String a = "aa";
  19.         String t = "a";
  20.         String b = "a"+t;
  21.         assertEquals("assertEquals", a, b) ; //A与B的关系是值相等,地址不相等,这个用例会成功
  22.     }

  23.     @Test
  24.     public void testSub() {
  25.         String a = "aa";
  26.         String t = "a";
  27.         String b = "a"+t;
  28.         assertSame("assertSame", a, b) ; //A与B的关系是值相等,地址不相等,这个用例会失败
  29.     }

  30.     @Test
  31.     public void testDiv() {
  32.         assertTrue("assertTrue",true);//用例成功
  33.         assertTrue("第二个为false失败",false);//用例失败
  34.     }

  35.     @Test
  36.     public void testDiv2() {
  37.         assertNull("assertNull",null);//用例成功
  38.         assertNull("第二个为notNull失败","a");//用例失败
  39.     }

  40.     @Ignore
  41.     public void testLoop() {
  42.     }

  43.     @Ignore
  44.     public void testUnCompleted() {
  45.     }

  46. }
复制代码
执行结果分析

这里我不再截图了

1 成功

2 失败,返回MSG为assertSame

3 第一个成功,第二个失败,返回MSG为第二个为false失败

4 第一个成功,第二个失败,返回MSG为第二个为notNull失败

断言是用来判断被测方法执行的结果与预期结果是否匹配



4 特殊的处理 (限时测试,异常测试)

1 Junit提供限时处理的机制。

@Test(timeout=1000) 单位毫秒



作者: 海鸥一飞    时间: 2018-3-7 16:14
当方法用时超过1000毫秒时,此方法会自动中止并执行失败
package com.logic.test;

import static org.junit.Assert.*;

import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Test;

import com.logic.LogicService;

public class LogicServiceTest {
    LogicService logserv ;

    @Before
    public void setUp() throws Exception {
        logserv = new LogicService();
    }

    @Ignore
    public void testAdd() {
    }

    @Ignore
    public void testSub() {
    }

    @Ignore
    public void testDiv() {
    }

    @Test(timeout=1000)
    public void testDiv2() {
        System.out.print("我不会超过1000毫秒的");
    }

    @Test(timeout=1000)
    public void testLoop() {
        logserv.loop(1, 1);//此方法使用的是死循环实现,所以一定会超过限时,超过限时后此方法会自动中止
    }

    @Ignore
    public void testUnCompleted() {
    }

}
执行结果

loop方法超时报错,div2方法不超时成功



2 Junit提供异常处理的机制。

@Test(expected=Exception.class) 其中Exception.class可以写的更加具体

测试
@Test(expected=Exception.class)
    public void testDiv() {
        System.out.print(logserv.div(3, 0));
    }

    @Test(expected=Exception.class)
    public void testDiv2() {
        System.out.print(logserv.div(3, 0));
    }
复制代码
执行结果

两个都通过



测试

复制代码
    @Test(expected=Exception.class)
    public void testDiv() {
        System.out.print(logserv.div(3, 0));
    }

    @Test
    public void testDiv2() {
        System.out.print(logserv.div(3, 0));
    }
执行结果

第一个通过 第二个不通过 异常为除数不能为zero



5 参数化配置 (@Parameters)   重要

进行单元测试的时候,通常一个方法需要好几个case进行测试,Junit提供参数化便于我们对方法进行
多种参数的组合测试

如果不使用参数化进行测试的话,那么我们的测试类会做的很臃肿

例如
@Test
    public void testAdd() {
        assertEquals("1+1 失败",2,logserv.add(1, 1) );
    }
    @Test
    public void testAdd1() {
        assertEquals("1+2 失败",3,logserv.add(1, 2) );
    }
    @Test
    public void testAdd2() {
        assertEquals("1+3 失败",3,logserv.add(1, 3) );
    }
    @Test
    public void testAdd3() {
        assertEquals("1+4 失败",3,logserv.add(1, 4) );
    }
这样的测试类显然看起来不是很理想,代码过于重复



参数化的实现过程(重要)

1 在测试类上增加

@RunWith(Parameterized.class)并引入

import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;

2 写上构造类的函数及定义入参

例如
@RunWith(Parameterized.class)
public class LogicServiceTest {
    LogicService logserv ;
    int parm1 ;//定义入参
    int parm2 ;//定义入参
    int res;//定义入参
   
   public LogicServiceTest(int parm1,int parm2,int res){//定义构造函数
       this.parm1=parm1;
       this.parm2=parm2;
       this.res=res;
   }
3 定义一个返回结果为collection类型的方法并写上@Parameters

注意 Arrays.asList()里面NEW的Object的要与定义的参数一一对应

例如
public class LogicServiceTest {
    LogicService logserv ;
    int parm1 ;//定义入参
    int parm2 ;//定义入参
    int res;//定义入参
   
   public LogicServiceTest(int parm1,int parm2,int res){//定义构造函数
       this.parm1=parm1;
       this.parm2=parm2;
       this.res=res;
   }
   
   @Parameters   
   public static Collection<Object[]> initParm(){
       return  Arrays.asList(new Object[][]{
          {1,1,2},{1,2,3},{1,3,4},{1,3,5}//{}里的参数一定要和构造函数一一对应
   });
   }
4 编写测试方法,调用参数

@Test
public void testAdd() {
assertEquals(res,logserv.add(parm1, parm2));
}
5 测试类代码全景(准备执行)

复制代码
package com.logic.test;

import static org.junit.Assert.*;

import java.util.Arrays;
import java.util.Collection;

import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;

import com.logic.LogicService;

@RunWith(Parameterized.class)
public class LogicServiceTest {
LogicService logserv;
int parm1;// 定义入参
int parm2;// 定义入参
int res;// 定义入参

public LogicServiceTest(int parm1, int parm2, int res) {// 定义构造函数
this.parm1 = parm1;
this.parm2 = parm2;
this.res = res;
}

@Parameters
public static Collection<Object[]> initParm() {
return Arrays.asList(new Object[][] { { 1, 1, 2 }, { 1, 2, 3 },
{ 1, 3, 4 }, { 1, 3, 5 } // {}里的参数一定要和构造函数一一对应
});
}

@Before
public void setUp() throws Exception {
logserv = new LogicService();
}

@Test
public void testAdd() {
assertEquals(res, logserv.add(parm1, parm2));
}

@Ignore
public void testSub() {
}
}
复制代码
6 执行结果






欢迎光临 51Testing软件测试论坛 (http://bbs.51testing.com/) Powered by Discuz! X3.2