目录
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命令。
- 常用场景下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()时,仍然不执行真实方法。
这就是全部模拟。
- 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中的三个测试用例。