目录 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
图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命令。
测试用例代码:
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(); ② }}该测试用例执行结果: 测试通过,且打印出以下内容:
initMocksadd(1, 1) = 1.0add(3, 4) = 7.0noreturn1() 被真实调用这样的结果,说明: 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(); }}运行结果:
initMocksadd(1, 1) = 1.0add(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()时,仍然不执行真实方法。
这就是全部模拟。
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中的三个测试用例。
新闻热点
疑难解答