我们的第一个例子非常简单,但是在现实情况下,你通常是在一些类里使用另外的一些类。在进行真正的测试之前,你可能需要做很多的工作,比喻说安置大量的环境代码,启动一种大型的、复杂的系统,可能是数据库、工作流或者是某一种类型的IDE环境,你的预设环境代码需要是系统进入某种特定的状态,以便按照测试所需要的方法进行响应。但是这种工作不大可能很快就能完成。
为了对一部分类进行单元测试,你需要建立和控制另外一些类。最好的办法就是为需要测试的类创建一个模拟对象。你可以自己手工的编写类,也可以使用EasyMock来产生这些对象。模拟对象提供了一种经过证明是成功的解决方案。
当我们很难或不可能为某种难以处理的资源创建需要的状态或者存取某种资源受限时,你就可以使用模拟对象。
模拟对象取代真实对象的位置,用于测试一些与真实对象进行交互或依赖于真实对象的功能。模拟对象背后的基本思想就是创建轻量级的、可控制的对象来代替为了编写测试为需要使用的对象。模拟对象还能够让你指定和测试你的代码与模拟对象本身之间的交互。
说的再直白一点,一个模拟对象就是一个简单的接口或者是类,在里面你可以定义一个特定的方法调用之后的简单的输出。
使用Junit和Easy Mock
从EasyMock到主页上下载EasyMock, 把下载得到的easymock.jar增加到你的classpath中。
创建一个Java工程JavaMockTest,创建下面的这些类。
IncomeCalculator是要被测试的类,这个类用来根据一个人的职位来计算一个人的工资的方法。
public enum Position {BOSS, PROGRAMMER, SURFER}
package income.exceptions;
public class PositionException extends RuntimeException {
private static final long serialVersionUID = 1L;
public PositionException(String message) {
super(message);
}
package income.exceptions;
public class CalcMethodException extends RuntimeException {
private static final long serialVersionUID = 1L;
public CalcMethodException(String message) {
super(message);
}
package income.method;
import income.Position;
public interface ICalcMethod {
public abstract double calc(Position position);
}
package income;
import income.exceptions.CalcMethodException;
import income.exceptions.PositionException;
import income.method.ICalcMethod;
public class IncomeCalculator{
private ICalcMethod calcMethod;
private Position position;
public void setCalcMethod(ICalcMethod calcMethod){
this.calcMethod = calcMethod;
}
public void setPosition(Position position){
this.position = position;
public double calc (){
if (calcMethod== null){
throw new CalcMethodException( "CalcMethod not yet maintained");
}
if (position== null){
throw new PositionException( "Position not yet maintained");
}
return calcMethod.calc(position);
}
使用Eclipse的JUnit功能为IncomeCalulator创建一个测试。在我的例子中,我同时创建一个新的源文件夹,名字为"test",我把我创建的测试类全部放到里面。
下面就是一个使用EasyMock创建的测试。
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import income.exceptions.CalcMethodException;
import income.exceptions.PositionException;
import income.method.ICalcMethod;
import org.easymock.EasyMock;
import org.junit.Before;
import org.junit.Test;
public class IncomeCalculatorTest {
private ICalcMethod calcMethod;
private IncomeCalculator calc;
@Before
public void setUp() throws Exception {
calcMethod = EasyMock.createMock(ICalcMethod. class);
calc = new IncomeCalculator();
}
@Test
public void testCalc1() {
// Setting up the expected value of the method call calc
EasyMock.expect(calcMethod.calc(Position.BOSS)).andReturn(70000.0).times(2);
EasyMock.expect(calcMethod.calc(Position.PROGRAMMER)).andReturn(50000.0);
// Setup is finished need to activate the mock
EasyMock.replay(calcMethod);
calc.setCalcMethod(calcMethod);
try {
calc.calc();
fail( "Exception did not occur");
} catch (PositionException e) {
}
calc.setPosition(Position.BOSS);
assertEquals(70000.0, calc.calc());
assertEquals(70000.0, calc.calc());
calc.setPosition(Position.PROGRAMMER);
assertEquals(50000.0, calc.calc());
calc.setPosition(Position.SURFER);
EasyMock.verify(calcMethod);
}
@Test(expected = CalcMethodException. class)
public void testNoCalc() {
calc.setPosition(Position.SURFER);
calc.calc();
}
@Test(expected = PositionException. class)
public void testNoPosition() {
EasyMock.expect(calcMethod.calc(Position.BOSS)).andReturn(70000.0);
EasyMock.replay(calcMethod);
calc.setCalcMethod(calcMethod);
calc.calc();
}
@Test(expected = PositionException. class)
public void testCalc2() {
// Setting up the expected value of the method call calc
EasyMock.expect(calcMethod.calc(Position.SURFER)).andThrow( new PositionException( "Don't know this guy")).times(1);
// Setup is finished need to activate the mock
EasyMock.replay(calcMethod);
calc.setPosition(Position.SURFER);
calc.setCalcMethod(calcMethod);
calc.calc();
}
expect方法告知EasyMock希望得到一个特定的方法,使用一些特定的参数,addReturn为某一个方法定义返回值。times方法定义了模拟对象将被调用几次。 reply方法需要在让模拟对象可用之前被调用。执行完测试之后,你可以调用verify方法来检查模拟对象是不是跟预期的一样被调用了。
1、mock次数的设置
times(int count)
:
times(int min, int max)
min
and
max
calls,
atLeastOnce
()
anyTimes
()
2、接口、抽象类、类的mock
org.easymock.classextension.EasyMock 被用来mock抽象类(abstract)和具体类
org.easymock.EasyMock被用来mock接口(interface)
3、创建多个 Mock 对象,EasyMock 提供了另外一种生成和管理 Mock 对象的机制
IMocksControl control = EasyMock.createControl(); java.sql.Connection mockConnection = control.createMock(Connection.class); java.sql.Statement mockStatement = control.createMock(Statement.class); java.sql.ResultSet mockResultSet = control.createMock(ResultSet.class); |
4、多个接口进行mock, 且之间存在相互关系, 例子如下:
- @Test
- public void testMock() throws Exception{
- Connection conn = EasyMock.createMock(Connection.class);
- PreparedStatement ps = EasyMock.createMock(PreparedStatement.class);
- ResultSet rs = EasyMock.createMock(ResultSet.class);
- EasyMock.expect(conn.prepareStatement("")).andReturn(ps).anyTimes();
- EasyMock.expect(ps.executeQuery()).andReturn(rs).anyTimes();
- EasyMock.expect(rs.getFloat(1)).andReturn(10.0f).anyTimes();
- EasyMock.replay(conn);
- EasyMock.replay(ps);
- EasyMock.replay(rs);
- System.out.println(conn.prepareStatement("").executeQuery().getFloat(1));
- }
本文转自 tianya23 51CTO博客,原文链接:http://blog.51cto.com/tianya23/268397,如需转载请自行联系原作者