JUnit4 单元测试注解详解

本文只解释junit中注解的相关知识,如果需要junit的断言知识请参考:

https://blog.csdn.net/qq_36098284/article/details/80684781

     ------------------------------------------------------------------------------------------------

一、常用注解详解

JUnit4通过注解的方式来识别测试方法。目前支持的主要注解有:

  • @BeforeClass 全局只会执行一次,而且是第一个运行
  • @Before 在测试方法运行之前运行
  • @Test 测试方法
  • @After 在测试方法运行之后允许
  • @AfterClass 全局只会执行一次,而且是最后一个运行
  • @Ignore 忽略此方法

 ---------------------------------------------------------------------------------------------------------------------

@Test
 
在junit3中,是通过对测试类和测试方法的命名来确定是否是测试,且所有的测试类必须继承junit的测试基类。在junit4中,定义一个测试方法变得简单很多,只需要在方法前加上@Test就行了。
 
注意:测试方法必须是public  void,即公共、无返回数据。可以抛出异常。
 
@Ignore
 
有时候我们想暂时不运行某些测试方法\测试类,可以在方法前加上这个注解。在运行结果中,junit会统计忽略的用例数,来提醒你。但是不建议经常这么做,因为这样的坏处时,容易忘记去更新这些测试方法,导致代码不够干净,用例遗漏。
 
@BeforeClass
 
当我们运行几个有关联的用例时,可能会在数据准备或其它前期准备中执行一些相同的命令,这个时候为了让代码更清晰,更少冗余,可以将公用的部分提取出来,放在一个方法里,并为这个方法注解@BeforeClass。意思是在测试类里所有用例运行之前,运行一次这个方法。例如创建数据库连接、读取文件等。
 
注意:方法名可以任意,但必须是public static void,即公开、静态、无返回。这个方法只会运行一次。
 
@AfterClass
 
跟@BeforeClass对应,在测试类里所有用例运行之后,运行一次用于处理一些测试后续工作,例如清理数据,恢复现场。
 
注意:同样必须是public static void,即公开、静态、无返回。这个方法只会运行一次。
 
@Before
 
与@BeforeClass的区别在于,@Before不止运行一次,它会在每个用例运行之前都运行一次。主要用于一些独立于用例之间的准备工作。
比如两个用例都需要读取数据库里的用户A信息,但第一个用例会删除这个用户A,而第二个用例需要修改用户A。那么可以用@BeforeClass创建数据库连接。用@Before来插入一条用户A信息。
 
注意:必须是public void,不能为static。不止运行一次,根据用例数而定。
 
@After
 
与@Before对应。
 
@Runwith
 
首先要分清几个概念:测试方法、测试类、测试集、测试运行器。
 
其中测试方法就是用@Test注解的一些函数
测试类是包含一个或多个测试方法的一个**Test.java文件
测试集是一个suite,可能包含多个测试类
测试运行器则决定了用什么方式偏好去运行这些测试集/类/方法。
 
而@Runwith就是放在测试类名之前,用来确定这个类怎么运行的。也可以不标注,会使用默认运行器。
 
常见的运行器有:
 
1. @RunWith(Parameterized.class) 参数化运行器,配合@Parameters使用junit的参数化功能
 
2.@RunWith(Suite.class)
@SuiteClasses({ATest.class,BTest.class,CTest.class})
 
测试集运行器配合使用测试集功能
 
3.@RunWith(JUnit4.class)
 
junit4的默认运行器
 
4.@RunWith(JUnit38ClassRunner.class)
 
用于兼容junit3.8的运行器
 
5.一些其它运行器具备更多功能。例如@RunWith(SpringJUnit4ClassRunner.class)集成了spring的一些功能
 
@Parameters
 
用于使用参数化功能。

  ---------------------------------------------------------------------------------------------------------------------

二、如何创建JUnit测试

我是应用的eclipse进行测试的。

首先建立一个Calculator类。

public class Calculator {
	private static int result; // 静态变量,用于存储运行结果
	
    public void add(int n) {
        result = result + n;
    }
    
    public void substract(int n) {
        result = result - 1;  //Bug: 正确的应该是 result =result-n
    }
    
    public void multiply(int n) {
    	
    }  // 此方法尚未写好
    
    public void divide(int n) {
        result = result / n;
    }
    
    public void square(int n) {
        result = n * n;
    }
    
    public void squareRoot(int n) {
        for (; ;) ;            //Bug : 死循环
    }
    
    public void clear() {     // 将结果清零
        result = 0;
    }
    
    public int getResult(){
        return result;
    }
} 

  -----------------------------------------------------------------------------------------------------------------------------

其次,导入包,这个不需要我们去下载,在我们的编译软件中可以自行导入。

选好一个项目,然后右键,选择build path  ---  add libraries ---- junit ---- next  --- finish



导入包成功,在项目的目录中会出现


  -----------------------------------------------------------------------------------------------------------------------------

最后,创建测试类

选择new --- JUnit Test Case ---- 然后写测试类就可以了。

  -----------------------------------------------------------------------------------------------------------------------------

我们首先测试前4个函数

TestCalculartor代码如下:

(要复制的话代码再最下面)



测试结果如下:

进度条是红颜色表示发现错误,具体的测试结果在进度条上面有表示“共进行了4个测试,其中1个测试被忽略,一个测试失败”。


针对有bug的第二个函数的具体错误如下:

注意下:@Ignore注解,如果是注解到了每个函数上,那么测试该时候,这个函数会被跳过。如果注释的是一个类,那么这个类会被跳过。

 -----------------------------------------------------------------------------------------------------------------------------

限时测试

对于那些逻辑很复杂,循环嵌套比较深的程序,很有可能出现死循环,因此一定要采取一些预防措施限时测试是一个很好的解决方案。

我们给这些测试函数设定一个执行时间,超过了这个时间,他们就会被系统强行终止,并且系统还会向你汇报该函数结束的原因是因为超时,这样你就可以发现这些Bug了

要实现这一功能,只需要给@Test标注加一个参数即可,代码如下:

  @Test(timeout = 1000)
    public void squareRoot() {
        calculator.squareRoot(4);
        assertEquals(2, calculator.getResult());
    }

解释:Timeout参数表明了你要设定的时间,单位为毫秒,因此1000就代表1秒。

Calculator中源码如下:


测试结果:



           ---------------------------------------------------------------------------------------------------------------

测试异常

JAVA中的异常处理也是一个重点,因此你经常会编写一些需要抛出异常的函数

那么,如果你觉得一个函数应该抛出异常,但是它没抛出,这算不算Bug呢?

这当然是Bug,并JUnit也考虑到了这一点,来帮助我们找到这种Bug。

例如,我们写的计算器类有除法功能,如果除数是一个0,那么必然要抛出“除0异常”。因此,我们很有必要对这些进行测试。代码如下:

 @Test(expected = ArithmeticException.class)
  public void divideByZero(){
        calculator.divide(0);
   }

如上述代码所示,我们需要使用@Test标注的expected属性,将我们要检验的异常传递给他,这样JUnit框架就能自动帮我们检测是否抛出了我们指定的异常。

代码:


   ----------------------------------------------------------------------------------------------------

参数化测试

我们可能遇到过这样的函数,它的参数有许多特殊值,或者说他的参数分为很多个区域。

例如,测试一下“计算一个数的平方”这个函数,暂且分三类:正数、0、负数。在编写测试的时候,至少要写3个测试,把这3种情况都包含了,这确实是一件很麻烦的事情。测试代码如下:

  public class AdvancedTest { 
        private static Calculator calculator = new Calculator();
        @Before
        public void clearCalculator(){
            calculator.clear();
        }

        @Test
        public void square1() {
            calculator.square(2);
            assertEquals(4, calculator.getResult());
        }     

        @Test    
        public void square2(){
            calculator.square(0);
            assertEquals(0, calculator.getResult());
        }

        @Test    
        public void square3(){
            calculator.square(-3);
            assertEquals(9, calculator.getResult());
        }
     }

为了简化类似的测试,JUnit4提出了“参数化测试”的概念,只写一个测试函数,把这若干种情况作为参数传递进去,一次性的完成测试。代码如下:

@RunWith(Parameterized.class)
    public class SquareTest{
        private static Calculator calculator = new Calculator();
        private int param;
        private int result;     

    @Parameters    
    public static Collection data() {
        return Arrays.asList(new Object[][]{
               {2, 4},
               {0, 0},
               {-3, 9},
        });
    }

    //构造函数,对变量进行初始化
    public SquareTest(int param, int result){
        this.param = param;
            this.result = result;
    }

    @Test    
    public void square(){
        calculator.square(param);
        assertEquals(result, calculator.getResult());
    }
 }

测试结果:



代码:


代码分析如下:

  • 为这种测试专门生成一个新的类,而不能与其他测试共用同一个类,此例中我们定义了一个SquareTest类。
  • 为这个类指定一个Runner,而不能使用默认的Runner,@RunWith(Parameterized.class)这条语句就是为这个类指定了一个ParameterizedRunner
  • 定义一个待测试的类,并且定义两个变量,一个用于存放参数,一个用于存放期待的结果。
  • 定义测试数据的集合,也就是上述的data()方法,该方法可以任意命名,但是必须使用@Parameters标注进行修饰
  • 定义构造函数,其功能就是对先前定义的两个参数进行初始化


 ----------------------------------------------------------------------------------------------------


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;
public class TestCalculator {
	private static Calculator calculator = new Calculator();
	   @Before
	   public void setUp() throws Exception {
	       calculator.clear();
	   }
	   @Test
	   public void testAdd() {
	       calculator.add(3);
	       calculator.add(4);
	       assertEquals(7, calculator.getResult());
	   }
	   @Test
	   public void testSubstract() {
	       calculator.add(8);
	       calculator.substract(3);
	       assertEquals(5, calculator.getResult());
	   }
	   //该函数被忽略
	   @Ignore("Multiply() Not yet implemented")
	   @Test
	   public void testMultiply() {
	       fail("Not yet implemented");
	   }
	   @Test
	   public void testDivide() {
	       calculator.add(8);
	       calculator.divide(2);
	       assertEquals(4, calculator.getResult());
	   }	
	   //限时测试
	   @Test(timeout = 1000)
	   public void squareRoot() {
	       calculator.squareRoot(4);
	       assertEquals(2, calculator.getResult());
	   }
	   //异常测试
	   @Test(expected = ArithmeticException.class)
	   public void divideByZero(){
	         calculator.divide(0);
	    }
	   
	    //参数化测试
	    @RunWith(Parameterized.class)
	    public static class SquareTest{
	        private final Calculator calculator = new Calculator();
	        private int param;
	        private int result;     

	    @Parameters    
	    public static Collection data() {
	        return Arrays.asList(new Object[][]{
	               {2, 4},
	               {0, 0},
	               {-3, 9},
	        });
	    }

	    //构造函数,对变量进行初始化
	    public SquareTest(int param, int result){
	        this.param = param;
	        this.result = result;
	    }

	    @Test    
	    public void square(){
	        calculator.square(param);
	        assertEquals(result, calculator.getResult());
	    }
	 }
}
 

本文参考:https://www.cnblogs.com/happyzm/p/6482886.html

https://www.cnblogs.com/tobey/p/4837495.html


猜你喜欢

转载自blog.csdn.net/qq_36098284/article/details/80684303