junit 单元测试指南

目录
1. junit的创建及执行 2
1.1maven依赖: 2
1.2创建一个unit test: 2
1.3 unit test执行方法: 6
1.3 maven打包时,不执行test的方法: 7
2. 常用场景下junit测试用例实例 8
2.1最基本的unit例子: 8
2.2私有方法的测试: 9
2.3引发异常方法的测试: 10
2.4存在依赖的方法测试: 12
2.4.1依赖隔离 12
2.4.2 mock测试实例 13
3. Junit运行器 17
3.1 Parameterized运行器 17
3.2 Suite运行器 19


1. junit的创建及执行
1.1maven依赖:

需要注意,我们创建一个maven project时,pom.xml中已存在junit的包,但是,是3.8版本的包,建议大家使用4.0以上的版本,本文档所有的例子,都是使用4.12版本的junit包。

1.2创建一个unit test:
 方法一:直接在src/test/java中创建一个test类。
 方法二:在被测类右键添加一个测试类,并选择测试类的路径为src/test/java,再选择要添加被测类中的哪几个方法。如图所示:

图1
这里写图片描述

图2
这里写图片描述
图3
这里写图片描述

图4
这里写图片描述

import static org.junit.Assert.*;
import org.junit.Test;
public class CalculaterTest2 extends Calculater {
    @Test   public void testAdd() {
        fail("Not yet implemented");
    }
    @Test
    public void testAddbystring() {
        fail("Not yet implemented");
    }
    @Test
    public void testDivide() {
        fail("Not yet implemented");
    }
    @Test
    public void testNoreturn() {
        fail("Not yet implemented");
    }
    @Test
    public void testNoreturn1() {
        fail("Not yet implemented");
    }
}

图5

可以看出,第二种方法,可以自动生成测试代码中,我们需要的最基本的代码,因此,这种方法相对好用。

1.3 unit test执行方法:
 方法一:单个测试用例,可以通过[RunAs->Junit Test]执行单元测试。
 方法二:可以通过Maven test执行project所有的测试用例
 方法三:Maven Build打包时,也会执行所有的unit test,如果有测试没有通过,则打包失败。
1.3 maven打包时,不执行test的方法:
 如果使用eclipse maven build打包,不想执行test,则勾选Skip Tests
这里写图片描述
 如果使用maven命令打包时,不想执行test,则使用mvn package –DskipTests命令。

  1. 常用场景下junit测试用例实例
    2.1最基本的unit例子:
    被测类及方法:
public class Calculater {
    //简单的public函数
    public double add( double number1 , double number2) {
        return number1 + number2;
        } 
}

测试用例代码:

import static org.junit.Assert.*;
import org.junit.Test;
/**
 * 
 * @author 32210
 *Test by default Runners
 */

public class CalculaterTest {
    private Calculater cal;
//通过@Test注释,将这个方法标准为一个单元测试方法。
    @Test                                       
    public void test() {
        cal = new Calculater();
        Double export = cal.add(7, 5);
//调用Assert模块的方法,检测结果的正确性。
        assertEquals(12,export,0);          
    }
}

2.2私有方法的测试:

被测类及方法:

public class Calculater {
//private函数
    private int sub(int number1, int number2){
        return (number1 - number2);
    }
}

测试用例代码:

import static org.junit.Assert.*;
import java.lang.reflect.Method;

import org.junit.Test;

public class privateTest {

    @Test
    public void testsub() throws Exception  {
        try{
        //获取目标类的class对象  
        Class<Calculater> class1 = Calculater.class;
        //获取目标类的实例  
        Object instance = class1.newInstance();  
        //获取私有方法  
        Method method = class1.getDeclaredMethod("sub", new Class[]{int.class,int.class});  
        //值为true时 反射的对象在使用时 让一切已有的访问权限取消  
        method.setAccessible(true); 
        Object result = method.invoke(instance, new Object[]{1,2}); 
        assertEquals(-1, result);
        }   

    }

2.3引发异常方法的测试:
1)Exception衍生的异常引发验证
被测类及方法:

public class Calculater {
public double addbystring( String number1 , String number2) throws TransIntException {
        try{
            return Integer.parseInt(number1) + Integer.parseInt(number2);
        }
        catch(Exception e){
            throw new TransIntException(number1 + "或者" + number2 + "无法转换为int类型");
        }
    }
}

异常类:

public class TransIntException extends Exception {        
    private String retCd ;  //异常对应的返回码  
    private String msgDes;  //异常对应的描述信息        
    public TransIntException() {  
        super();      }  

    public TransIntException(String message) {  
        super(message);  
        msgDes = message;  
    }    
    public TransIntException(String retCd, String msgDes) {  
        super();  
        this.retCd = retCd;  
        this.msgDes = msgDes;  
    }    
    public String getRetCd() {  
        return retCd;  
    }    
    public String getMsgDes() {  
        return msgDes;  
    }  
}  

测试用例代码:

import static org.junit.Assert.*;
import org.junit.Test;
public class CalculaterTest1 {
    private Calculater col = new Calculater();
@Test()
    public void testAddbystring() {

         try {
            Double export = col.addbystring("a", "b");
            fail("Expected an TransIntException to be thrown");
        } catch (Exception e) {
            // TODO Auto-generated catch block
//调用Assert模块的方法,检测结果的正确性。
            assertEquals(e.getClass(), TransIntException.class);
        }
    }
}

2)验证引发RuntimeException衍生的异常
被测类及方法:

public class Calculater {
public int divide(int number1, int number2) {
        return number1 / number2;
    }
}

测试用例代码:

public class CalculaterTest1 {
    private Calculater cal = new Calculater();
//通过@Test的expected参数来制定预期异常
@Test(expected= ArithmeticException.class)
    public void testdivide(){
        cal.divide(1, 0);
    }
}

2.4存在依赖的方法测试:
2.4.1依赖隔离
对于存在依赖的类方法,很多时候需要先模拟一个类来代替依赖的类。

在java中,实现这种模拟的模块有EasyMock,Jmock和mockito,或许还有别的,在这里,就只介绍一下时下最流行最常用的mockito。

Mockito中,可以用三种方式来定义mock对象:
1) 直接定义对象:
private Calculater mocksum1 = Mockito.mock(Calculater.class);
2) 用 @Spy 注解定义mock对象,实现部分方法的mock。
3) 用 @Mock 注解定义mock对象,实现全部方法的mock。

要使模拟对象,在被测对象中有效,需要将模拟对象注入到被测对象中,mockito中,可使用 @InjectMocks 将模拟对象自动注入到被测对象中,但它只对第二,第三种方法定义的模拟对象有效,也就是说,用第一种方法模拟出来的对象,如果想要对被测有效,就需要在被测类中,定义set函数,那么,我们就需要为了单元测试去改我们的代码,因此,不建议使用第一种方法定义模拟对象,在这里也不介绍这种方法。

第二、三种方法,会在以下例子中进行详细介绍,可以很容易看出,什么叫部分mock,什么叫全部mock。

2.4.2 mock测试实例

被测类方法代码:

//被依赖的类方法
public class Calculater {
    //简单的public函数
    public double add( double number1 , double number2) {
        return number1 + number2;
        }
    public void noreturn(){
        //do something,but no return
        System.out.println("noreturn() 被真实调用");
    }
    public void noreturn2(){
        //do something,but no return
        System.out.println("noreturn1() 被真实调用");
    }
}
//被测类方法
public class PrintSum {
    private Calculater cal = new Calculater();

    public void print(){        
        Double result1 = cal.add(1, 1);
        System.out.println("add(1, 1) = " + result1);
        Double result2 = cal.add(3, 4);
        System.out.println("add(3, 4) = " + result2);
    //调用void的函数
        cal.noreturn(); 
cal.noreturn1();        
    }
}

测试用例代码:
1) 用 @Spy模拟对象,是部分被模拟,没有指定行为或返回值的方法,仍然是使用真实的方法。

import org.mockito.InjectMocks;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.mockito.Spy;
import org.junit.Before;
import org.junit.Test;

public class PrintSumTest { 
    //@Before注释定义下的方法,会在测试用例执行前执行。
    @Before  
    public void initMocks() {  
        MockitoAnnotations.initMocks(this);  
        System.out.println("initMocks");
    }
    //@InjectMocks 自动将mock对象注入被测类,如果不使用这个注解,对象sum里的依赖,//仍然执行真实依赖
    @InjectMocks
    private PrintSum sum;   

    //@Spy 的模拟对象,部分函数被模拟
    @Spy private Calculater mocksum;

    @Test
    public void testPrint() {
        //设定返回值
        Mockito.when(mocksum.add(1, 1)).thenReturn((double) 1);
        //当依赖的函数没有返回值,可以用doNothing进行隔离
        Mockito.doNothing().when(mocksum).noreturn();
        sum.print();    
        //验证add函数是否有被调用
        Mockito.verify(mocksum).add(1, 1);
        //验证add函数被调用的次数是否如预期
        Mockito.verify(mocksum, Mockito.times(1)).add(1, 1);      ①
        Mockito.verify(mocksum, Mockito.times(1)).noreturn();     ②

    }
}

该测试用例执行结果:
测试通过,且打印出以下内容:

initMocks
add(1, 1) = 1.0
add(3, 4) = 7.0
noreturn1() 被真实调用

这样的结果,说明:
a. 由于测试代码中,指定了add(1, 1)的返回值( Mockito.when(mocksum.add(1, 1)).thenReturn((double) 1)),因此, 当调用sum.print()时,该函数里的add(1, 1)调用的是模拟方法,返回的结果是模拟对象指定给它的返回值。
b. add(3, 4)没有被指定行为或返回值,当调用sum.print()时,该函数里的add(3, 4)调用的是真实方法,返回的是真实方法运行后返回的结果。
c. 被测代码中,指定了noreturn()的行为
(Mockito.doNothing().when(mocksum).noreturn()),因此,当调用sum.print()时,该函数里的noreturn()调用的是模拟方法。
d. 被测代码中,没有指定noreturn1()的行为,因此当调用sum.print()时,该函数里的noreturn1()调用的是真实方法。
这就是部分mock。
另外:
①的位置,验证了mocksum.add(1, 1)被调用了一次。
②的位置,验证了mocksum.noreturn()被调用了一次。

2) 用@Mock模拟对象,是全部被模拟。

public class PrintSumTest { 
    @Before  
    public void initMocks() {  
        MockitoAnnotations.initMocks(this);  
        System.out.println("initMocks");
    }
    //@InjectMocks 自动将mock对象注入被测类
    @InjectMocks
    private PrintSum sum;
    //@Mock 自动生成mock对象,所有的函数,都是模拟对象的函数,没有指定return值,则返回空、0、null等值
    //@Mock 对象里的void函数,如果要隔离依赖,也不需要用Mockito.doNothing() 
    @Mock private Calculater mocksum;
    @Test
    public void testPrint() {       
        Mockito.when(mocksum.add(1, 1)).thenReturn((double) 1);
        System.out.println(mocksum.add(1, 1));
        sum.print();            
    }
}

运行结果:

initMocks
add(1, 1) = 1.0
add(3, 4) = 0.0

这样的结果,说明:
a. mocksum.add(1, 1)被指定返回值,当调用sum.print()时,该函数里的add(1, 1)调用的是模拟方法,返回的结果是模拟对象指定给它的返回值。
b. mocksum.add(3, 4)没有指定返回值,但调用sum.print()时,该函数里的add(3, 4)调用的仍然是模拟方法,没有指定时,返回的是0.
c. mocksum.noreturn()和mocksum.noreturn1()都没有指定doNothing行为,但是,调用sum.print()时,仍然不执行真实方法。

这就是全部模拟。

  1. Junit运行器
    当不指定任何运行器时,junit会使用默认的运行器。Junit4一共有4种运行器,分别是:
    org.junit.internal. runners .JUnic38ClassRunner
    org,junit.runners.Junit4
    org.junit.runners.Parameterized
    org.junit.runners.Suite

这里,我们介绍后两种
3.1 Parameterized运行器
Parameterized(参数化)的测试运行器,允许使用不同的参数多次运行同一个测试,可以简化测试代码。例子如下:
被测代码:

public class Calculater {
    //简单的public函数
    public double add( double number1 , double number2) {
        return number1 + number2;
        }
}

测试代码:

import static org.junit.Assert.*;
import java.util.Arrays;
import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
@RunWith(Parameterized.class)
public class ParameterizedTest {
    private double result;
    private double value1;
    private double value2;      
    @Parameters
    public static List<Integer[]> getTestParamenter() {
        return Arrays.asList(new Integer[][]{
                {3,2,1},{5,4,1},{8,4,4}
        }); }   
    public ParameterizedTest(double expected,
    double value1, double value2) {
        this.result = expected;
        this.value1 = value1;
        this.value2 = value2;
}   
    @Test
    public void testAdd() {
        Calculater cal = new Calculater();
        assertEquals(result,cal.add(value1, value2), 0);
    }
}

3.2 Suite运行器
Suite的目的是,将多个测试类作为一个集合进行测试。
例子如下:

import org.junit.runner.RunWith;
import org.junit.runners.Suite;
import org.junit.runners.Suite.SuiteClasses;

@RunWith(Suite.class)
@SuiteClasses({ CalculaterTest.class, ParameterizedTest.class })
public class AllTests {
}

运行该类,运行结果为:

这里写图片描述
说明执行了4个测试用例,其中包含CalculaterTest.class中的一个测试用例和
ParameterizedTest.class中的三个测试用例。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值