用powermock mock Dao Http请求 log4j

qinshang 2013-12-19

junit中比较常用的三种情形:

1.mocklog4j,对log进行测试

2.mockDAO,使得测试脱离真实的DB环境,不需要连数据库

3.mockHttp,使得测试脱离外部环境,不需要真的去进行Http请求

package com.project.service;

import com.project.bean.User;
import com.project.dao.UserDAO;
import com.project.mock.MockLog;

import org.apache.commons.logging.LogFactory;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.StatusLine;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.impl.client.DefaultHttpClient;
import org.easymock.EasyMock;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.api.easymock.PowerMock;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;

@RunWith(PowerMockRunner.class)
@PrepareForTest({TargetService.class, LogFactory.class, DefaultHttpClient.class})	// 告诉PowerMock哪些类需要被mock
public class TargetServiceTest {

	private MockLog mLog;
	
	@Before
	public void setUp() {
		
		// 实例化自己的mock log对象
		mLog = new MockLog();
		// 通过反射对TargetService中的变量log进行mock
		Whitebox.setInternalState(TargetService.class, "log", mLog);
	}
	
	/**
	 * mock log4j示例
	 * @throws Exception
	 */
	@Test 
	public void testLog4j() throws Exception {
		// mock
		TargetService target = PowerMock.createPartialMock(TargetService.class, "method1");
		PowerMock.expectPrivate(target, "method1").andThrow(new IOException());
		PowerMock.replay(target);
		
		// 验证是否抛出IOException
		Assert.assertEquals(IOException.class , mLog.getExceptionList().get(0).getClass());
		// 验证log的条数是否正确
		Assert.assertEquals(1, mLog.getLogList().size());
		// 验证log输出的错误信息是否正确
		Assert.assertEquals("system error" , mLog.getLogList().get(0));
		
		PowerMock.verify(target);
	}
	
	/**
	 * mock DAO,使得测试脱离真实的DB环境,不需要连数据库
	 * @throws Exception
	 */
	@Test 
	public void testMockDAO() throws Exception {
		// mock
		TargetService target = PowerMock.createPartialMock(TargetService.class, "method1", "method2");
		// mock userDAO
		UserDAO userDAO = PowerMock.createMock( UserDAO.class );
		// 返回数据的准备
		User user = new User();
		int userId = 28;
		user.setNickname( "ニックネーム" );
		user.setUserid( userId );
		// 既然是mock的dao,那么想得到什么都由自己定,脱离了真实的DB环境
		EasyMock.expect( userDAO.getUser( EasyMock.anyInt() ) ).andReturn( user );
		PowerMock.replay(userDAO);
		// 将mock userDao注入到目标类中
		target.setUserDAO( userDAO );
		
		PowerMock.replay(target);
		// Dao已经被mock了,接下来根据自己的需求做一些事情
		...
		
		// 验证正确性
		Assert.assertEquals(expected, actual);
		
		PowerMock.verify(userDAO);
		PowerMock.verify(target);
	}
	
	/**
	 * mock Http,使得测试脱离外部环境,不需要真的去进行Http请求
	 * @throws Exception
	 */
	@Test 
	public void testMockHttp() throws Exception {
		// mock HttpClient
		DefaultHttpClient httpClient = PowerMock.createMock(DefaultHttpClient.class);
		PowerMock.expectNew(DefaultHttpClient.class).andStubReturn(httpClient);
		
		// mock HttpResponse
		HttpResponse response = PowerMock.createMock(HttpResponse.class);
		EasyMock.expect(httpClient.execute(EasyMock.anyObject(HttpUriRequest.class))).andStubReturn(response);
		
		StatusLine statusLine = PowerMock.createMock(StatusLine.class);
		EasyMock.expect(response.getStatusLine()).andStubReturn(statusLine);
		
		// 设定期望的http status code,也可以是404,500等
		EasyMock.expect(statusLine.getStatusCode()).andStubReturn(200);
		
		HttpEntity entity = PowerMock.createMock(HttpEntity.class);
		EasyMock.expect(response.getEntity()).andStubReturn(entity);
		// 设定期望的http响应结果,这里用一个字符串来作为返回值
		InputStream inputStream = new ByteArrayInputStream("code:E01".getBytes());
		EasyMock.expect(entity.getContent()).andReturn(inputStream);
		
		// 已经得到了期望的Http请求结果,接下来根据自己的需求做一些事情
		...
		
		PowerMock.replayAll();
		// 验证正确性
		Assert.assertEquals(expected, actual);
		PowerMock.verifyAll();
	}
}

测试log4j需要的自定义的mocklog类:

package com.project.mock;

import java.util.ArrayList;
import java.util.List;

import org.apache.commons.logging.Log;

	/**
	 * 这是一个自定义的log类,用于mock真实的log4j对象,它可以模拟log4j的各种动作,并且可以订制各种我们希望的动作
	 * 这里只模仿两个动作,一个是单纯的接收一条message,一个是接收message同时接收一个exception
	 * 这样一来,真实代码中要用到log4j对象记录log的地方,都被这个mock log给替换了,并且所有记录的信息都被记录到这个对象中,供测试用
	 * @throws Exception
	 */
public class MockLog implements Log {
	private List<Object> logList = new ArrayList<Object>();
	private List<Throwable> exceptionList = new ArrayList<Throwable>();

	@Override
	public void debug(Object arg0) {
		logList.add(arg0);
	}

	@Override
	public void debug(Object arg0, Throwable arg1) {
		logList.add(arg0);
		exceptionList.add( arg1 );
	}

	@Override
	public void error(Object arg0) {
		logList.add(arg0);
	}

	@Override
	public void error(Object arg0, Throwable arg1) {
		logList.add(arg0);
		exceptionList.add( arg1 );
	}

	@Override
	public void fatal(Object arg0) {
	}

	@Override
	public void fatal(Object arg0, Throwable arg1) {
		logList.add(arg0);
		exceptionList.add( arg1 );
	}

	@Override
	public void info(Object arg0) {
		logList.add(arg0);
	}

	@Override
	public void info(Object arg0, Throwable arg1) {
		logList.add(arg0);
		exceptionList.add( arg1 );
	}

	@Override
	public boolean isDebugEnabled() {
		return false;
	}

	@Override
	public boolean isErrorEnabled() {
		return false;
	}

	@Override
	public boolean isFatalEnabled() {
		return false;
	}

	@Override
	public boolean isInfoEnabled() {
		return false;
	}

	@Override
	public boolean isTraceEnabled() {
		return false;
	}

	@Override
	public boolean isWarnEnabled() {
		return false;
	}

	@Override
	public void trace(Object arg0) {
		logList.add(arg0);
	}

	@Override
	public void trace(Object arg0, Throwable arg1) {
		logList.add(arg0);
		exceptionList.add( arg1 );
	}

	@Override
	public void warn(Object arg0) {
		logList.add(arg0);
	}

	@Override
	public void warn(Object arg0, Throwable arg1) {
		logList.add(arg0);
		exceptionList.add( arg1 );
	}

	public List<Object> getLogList() {
		return logList;
	}

	public void setLogList(List<Object> logList) {
		this.logList = logList;
	}

	public void setExceptionList( List<Throwable> exceptionList ) {
		this.exceptionList = exceptionList;
	}

	public List<Throwable> getExceptionList() {
		return exceptionList;
	}
}