下面,我将讲述如何使用JUnit和EasyMock框架来进行单元测试。
在现实情况下,我们通常是在一些类里使用另外的一些类。在进行真正的测试之前,你可能需要做很多的工作,比喻说安置大量的环境代码,启动一种大型的、复杂的系统,可能是数据库、功过刘或者是某一种类型的IDE环境,你的预设环境代码需要是系统进入某种特定的状态,以便按照测试所需要的方法进行响应。但是这种工作不大可能很快就能完成。
为了对一部分类进行单元测试,你需要建立和控制另外一些类。最好的办法就是为需要测试的类创建一个模拟对象。你可以自己手工的编写类,也可以使用EasyMock来产生这些对象。
模拟对象提供了一种经过证明是成功的解决方案。当我们很难或不可能为某种难以处理的资源创建需要的状态或者存取某种资源受限时,你就可以使用模拟对象。
模拟对象取代真实对象的位置,用于测试一些与真实对象进行交互或依赖于真实对象的功能。模拟对象背后的基本思想就是创建轻量级的、可控制的对象来代替为了编写测试为需要使用的对象。模拟对象还能够让你指定和测试你的代码与模拟对象本身之间的交互。
说的再直白一点,一个模拟对象就是一个简单的接口或者是类,在里面你可以定义一个特定的方法调用之后的简单的输出。
EasyMock的使用,需要两个jar包,Junit和EasyMock的jar包,
你可以自己从官网上分别下载它们,也可以从下面的地址下载:
Junit4.11:http://download.csdn.net/detail/zyh5540/5860807
EasyMock3.2:http://download.csdn.net/detail/zyh5540/5860785
其中使用Junit时可能提示缺少hamcrest的jar包,这时你需要导入该jar包,同时你可以去Junit的官网下载hamcrest的jar包,此时需要主义Junit和harmcrest之间的版本号对应关系,这个也可以从官网上查得到。如果是从我上边提供的链接下载的,不需要单独在下载了。
package easymock.income; /** * Position类表示职位信息,总共有三类:老板,程序猿,管理员 */ public enum Position { BOSS,PROGRAMMER,MANAGER }
package easymock.income.method; import easymock.income.Position; /** * 定义的计算工资的接口 * 并有一个抽象方法:计算工资 */ public interface ICalMethod { public abstract double cal(Position position); }
package easymock.income; import easymock.income.Position; import easymock.income.exception.CalMethodException; import easymock.income.exception.PositionException; import easymock.income.method.ICalMethod; /** * 收入(工资)计算的实现类,该类根据计算方法和职员的职位 * 计算该职员的工资 */ public class IncomeCaculator { private ICalMethod method; private Position position; public void setMethod(ICalMethod method) { this.method = method; } public void setPosition(Position position) { this.position = position; } public double cal() { //计算的方法类为空,抛计算方法空异常 if(null == method){ throw new CalMethodException("cal method is null"); } //当职位为空时,抛职位为空异常 if(null == position) { throw new PositionException("position is null"); } //返回该职员的工资 return method.cal(position); } }
package easymock.income; import easymock.income.exception.CalMethodException; import easymock.income.exception.PositionException; import easymock.income.method.ICalMethod; /** * 收入(工资)计算的实现类,该类根据计算方法和职员的职位 * 计算该职员的工资 */ public class IncomeCaculator { private ICalMethod method; private Position position; public void setMethod(ICalMethod method) { this.method = method; } public void setPosition(Position position) { this.position = position; } public double cal() { //计算的方法类为空,抛计算方法空异常 if(null == method){ throw new CalMethodException("cal method is null"); } //当职位为空时,抛职位为空异常 if(null == position) { throw new PositionException("position is null"); } //返回该职员的工资 return method.cal(position); } }
以下是方法为空的异常类和职位为空的异常类:
package easymock.income.exception; public class CalMethodException extends RuntimeException { private static final long serialVersionUID = 1L; public CalMethodException(String message) { super(message); } }
package easymock.income.exception; public class PositionException extends RuntimeException { private static final long serialVersionUID = 1L; public PositionException(String message) { super(message); } }
package easymock; import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; import org.easymock.EasyMock; import org.junit.Before; import org.junit.Test; import easymock.income.IncomeCaculator; import easymock.income.Position; import easymock.income.exception.CalMethodException; import easymock.income.exception.PositionException; import easymock.income.method.ICalMethod; /** * EasyMock的使用步骤: * 使用 EasyMock 生成 Mock 对象 * 设定 Mock 对象的预期行为和输出; * 将 Mock 对象切换到 Replay 状态; * 调用 Mock对象方法进行单元测试; 对 Mock 对象的行为进行验证。 * */ public class EasyMockTest { private ICalMethod calMethod; private IncomeCaculator caculator; @Before public void setUpBeforeClass() throws Exception { //第一步:生成Mock对象 calMethod = EasyMock.createMock(ICalMethod.class); caculator = new IncomeCaculator(); } @Test public void testCacl1() { /** * 设定 Mock 对象的预期行为和输出 */ //计算方法的参数是Position.BOSS, //andReturn(),期望calMethod方法返回的是7000.0 //times(),该方法执行次数为两次 EasyMock.expect(calMethod.cal(Position.BOSS)).andReturn(7000.0) .times(2); //默认执行次数为一次 EasyMock.expect(calMethod.cal(Position.PROGRAMMER)).andReturn(5000.0); //将 Mock 对象切换到 Replay 状态 EasyMock.replay(calMethod); /** * 调用 Mock 对象方法进行单元测试 * 代码的参数设置及返回结果应参考上面预期行为的设定 * 方法的执行顺序应和上面的一致 */ caculator.setMethod(calMethod); try { caculator.cal(); fail("exception is not occur"); } catch (PositionException e) { System.out.println("position exception"); } caculator.setPosition(Position.BOSS); //判断结果是否和预期的一样 //第一次预期的执行 //这里应该让caculator.cal()方法执行两次,这个次数和上面设定的次数一致 assertEquals(caculator.cal(), 7000.0, 0); assertEquals(caculator.cal(), 7000.0, 0); //第二次预期的执行 caculator.setPosition(Position.PROGRAMMER); assertEquals(5000.0, caculator.cal(), 0); caculator.setPosition(Position.MANAGER); //对 Mock对象的行为进行验证,验证是否两次预期都已执行 EasyMock.verify(calMethod); } //测试能否抛出计算方法为空的异常 @Test(expected = CalMethodException.class) public void testNoCalc() { caculator.setPosition(Position.MANAGER); caculator.cal(); } @Test(expected = PositionException.class) public void testNoPosition() { EasyMock.expect(calMethod.cal(Position.BOSS)).andReturn(7000.0); EasyMock.replay(calMethod); caculator.setMethod(calMethod); caculator.cal(); } @Test(expected = PositionException.class) public void testCalc2() { //andThrow(),预期抛出一个异常 EasyMock.expect(calMethod.cal(Position.MANAGER)) .andThrow(new PositionException("Don't know this guy")) .times(1); EasyMock.replay(calMethod); caculator.setPosition(Position.MANAGER); caculator.setMethod(calMethod); caculator.cal(); } }