Java单元测试打桩-mockito,PowerMockito简单使用,模拟方法内new对象

0、需要的jar

<dependency>
	    <groupId>junit</groupId>
	    <artifactId>junit</artifactId>
	    <version>4.12</version>
	    <scope>test</scope>
	</dependency>
	
	<dependency>
	    <groupId>org.mockito</groupId>
	    <artifactId>mockito-all</artifactId>
	    <version>2.0.2-beta</version>
	    <scope>test</scope>
	</dependency>

	<dependency>
	    <groupId>org.powermock</groupId>
	    <artifactId>powermock-module-junit4</artifactId>
	    <version>1.7.4</version>
	    <scope>test</scope>
	</dependency>

	<dependency>
	    <groupId>org.powermock</groupId>
	    <artifactId>powermock-api-mockito</artifactId>
	    <version>1.7.4</version>
	    <scope>test</scope>
	</dependency>

1、简单单元测试

package test;

public class ProcessDB {

	public String getResultOfConnectDB() {
		
		return "haha, Really went to the database";
	}

}
package test;

public class BussinessService {

	private ProcessDB processDB;
	
	public BussinessService(ProcessDB db){
		this.processDB = db;
	}
	public String testedMehtod(){
		return processDB.getResultOfConnectDB();
	}
}

测试类

package test

import org.junit.Assert;
import org.junit.Test;


public class BussinessTest {


	@Test
	public void testFuction(){
		ProcessDB db = new ProcessDB();
		BussinessService bs = new BussinessService(db);
		System.out.println(bs.testedMehtod());
		Assert.assertEquals("haha, Really went to the database", bs.testedMehtod());
	}
	
	
}

正常的一个单元测试,测试一下正常流程,对结果进行断言。

2、mockito模拟对象

大多数时候,所测试对象依赖于一些例如数据库连接,中间件等其他远程服务,对测试造成了影响,下面用mockito模拟连接数据库的过程

import static org.junit.Assert.*;
import org.junit.Before;
import org.junit.Test;
public class BussinessTest {

	private ProcessDB mockDB;
	private BussinessService bs;
	@Before
	public void setUp() throws Exception {
		mockDB = mock(ProcessDB.class);
		String aa = "haha, everything is fake";
	    when(mockDB.getResultOfConnectDB()).thenReturn(aa);
	    bs = new BussinessService(mockDB);
	}
	
	@Test
	public void mockConnectDB(){
		assertEquals("haha, everything is fake", bs.testedMehtod());
	}
}

这样就模拟了连接数据库的过程输出,本来真实连接数据库的时候输出“haha, Really went to the database“,但是真实连接数据库来测试比较麻烦,所以现在被模拟对象取代,输出你自己造的数据"haha, everything is fake"。

不过这一切都建立在你mock的对象是你被测试对象的属性变量,而不是在测试方法内部自己new出来的ProcessDB对象。下面看方法内部new对象,使用mockito模拟对象会怎样

3、mockito模拟方法内部new的对象

被测试类简单修改:

package test;

public class BussinessService {

	
	public String testedMehtod(){
		ProcessDB processDB = new ProcessDB();
		return processDB.getResultOfConnectDB();
	}
}

测试:

package test;

import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static org.junit.Assert.*;
import org.junit.Before;
import org.junit.Test;
public class BussinessTest {

	private ProcessDB mockDB;
	private BussinessService bs;
	@Before
	public void setUp() throws Exception {
		mockDB = mock(ProcessDB.class);
		String aa = "haha, everything is fake";
	    when(mockDB.getResultOfConnectDB()).thenReturn(aa);
	    bs = new BussinessService();
	}
	
	@Test
	public void mockConnectDB(){
		System.out.println(bs.testedMehtod());
		assertEquals("haha, everything is fake", bs.testedMehtod());
	}
}

测试失败:


4、PowerMockito模拟方法内部创建出来的对象

package test;

public class ProcessDB {

	public static ProcessDB getInstance(){
		return new ProcessDB();
	}
	public String getResultOfConnectDB() {
		
		return "haha, Really went to the database";
	}

}
package test;

public class BussinessService {

	
	public String testedMehtod(){
		ProcessDB processDB = ProcessDB.getInstance();
		return processDB.getResultOfConnectDB();
	}
}

有时候被测试类存在这样的写法,上述mockito又不管用,使用PowerMockito来模拟

package test;

import static org.junit.Assert.assertEquals;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;

@RunWith(PowerMockRunner.class)  
@PrepareForTest(ProcessDB.class) 
public class BussinessTest {

	private ProcessDB mockDB;
	private BussinessService bs;
	
	
	@Test
	public void mockConnectDB(){
		PowerMockito.mockStatic(ProcessDB.class);
		mockDB = PowerMockito.mock(ProcessDB.class);  
	    bs = new BussinessService();  
	    PowerMockito.when(ProcessDB.getInstance()).thenReturn(mockDB); 
	    String aa = "haha, everything is fake";
	    PowerMockito.when(mockDB.getResultOfConnectDB()).thenReturn(aa);
		System.out.println(bs.testedMehtod());
		assertEquals("haha, everything is fake", bs.testedMehtod());
	}
}

这样又可以测试通过,模拟了连接数据库的过程,特别注意头顶的两个注解,@RunWith(PowerMockRunner.class)  

@PrepareForTest(ProcessDB.class) 。分两步:第一步模拟出假对象PowerMockito.when(ProcessDB.getInstance()).thenReturn(mockDB); ,第二步模拟假对象调用的方法PowerMockito.when(mockDB.getResultOfConnectDB()).thenReturn(aa);

5、仿照上面解决3中的问题,内部直接使用new对象而不是通过静态方法getInstance()

package test;

public class BussinessService {

	
	public String testedMehtod(){
		ProcessDB processDB = new ProcessDB();
		return processDB.getResultOfConnectDB();
	}
}
测试类:
package test;

import static org.junit.Assert.assertEquals;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;

@RunWith(PowerMockRunner.class)  
@PrepareForTest(ProcessDB.class) 
public class BussinessTest {

	private ProcessDB mockDB;
	private BussinessService bs;
	
	
	@Test
	public void mockConnectDB(){
		mockDB = PowerMockito.mock(ProcessDB.class);  
	    bs = new BussinessService();  
	    try {
			PowerMockito.whenNew(ProcessDB.class).withNoArguments().thenReturn(mockDB);
		} catch (Exception e) {
			e.printStackTrace();
		} 
	    String aa = "haha, everything is fake";
	    PowerMockito.when(mockDB.getResultOfConnectDB()).thenReturn(aa);
		System.out.println(bs.testedMehtod());
		assertEquals("haha, everything is fake", bs.testedMehtod());
	}
}

这样看似与4相似,但是结果测试又通不过,输出的又是"haha, Really went to the database",加上PowerMockito.mockStatic(ProcessDB.class)也不管用。

6、真正解决3中的new对象的问题

其实5已经基本上实现了,经过一段时间的排查,原来问题是出在头顶上的两个注解上

package test;

import static org.junit.Assert.assertEquals;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;

@RunWith(PowerMockRunner.class)  
@PrepareForTest({ProcessDB.class, BussinessService.class}) 
public class BussinessTest {

	private ProcessDB mockDB;
	private BussinessService bs;
	
	
	@Test
	public void mockConnectDB(){
		mockDB = PowerMockito.mock(ProcessDB.class);  
	    bs = new BussinessService();  
	    try {
			PowerMockito.whenNew(ProcessDB.class).withNoArguments().thenReturn(mockDB);
		} catch (Exception e) {
			e.printStackTrace();
		} 
	    String aa = "haha, everything is fake";
	    PowerMockito.when(mockDB.getResultOfConnectDB()).thenReturn(aa);
		System.out.println(bs.testedMehtod());
		assertEquals("haha, everything is fake", bs.testedMehtod());
	}
}

测试通过,一切OK

这样应该可以实现模拟对象的各种情况了,即使代码不规范,喜欢到处new依赖对象而不是将依赖对象注入服务,也可以轻松模拟对象,模拟连接远程服务,构造自己想要的数据从而完成测试自己代码逻辑。

7、流程测试的最后一步是外部依赖时的验证(例如发送到kafka等)

package mockito;

import java.util.LinkedList;

public class Verify {

	private LinkedList k ;
	public Verify(LinkedList k){
		this.k = k;
	}
	
	public void test(){
		k.add("d");
		k.add("b");
		System.out.println(k.get(0));
	}
}
package mockito;

import java.util.LinkedList;
import static org.mockito.Mockito.*;
import org.junit.Test;

public class VeriryTestTest {
    @Test
	public void testt(){
	 LinkedList mockedList = mock(LinkedList.class);
	 when(mockedList.get(anyInt())).thenReturn("first");
	 new Verify(mockedList).test();
	 verify(mockedList).get(anyInt());

	}
}
此测试LinkedList相当于外部依赖,最后验证依赖的某个方法是否被调用过,如果有机会调用说明流程上基本上没什么问题,验证不严谨。

猜你喜欢

转载自blog.csdn.net/ccityzh/article/details/80139807