1. 单元测试就是对软件的一个单元进行隔离测试,然而大多数软件的各个单元并不是孤立,它们相互协作,有着千丝万缕的联系,因此为了对一个单元进行测试,我们就必须对这个单元依赖的其他单元进行模拟。 EasyMock 是第一个动态模拟对象生成器,它将程序员从手工编写模拟对象的繁琐中解脱出来。在进行测试之前,先要下载easyMock的包并且加载到库中
2. 编写工资计算方法的接口 ISalaryCalculator
package org.liky.junit.easymock;
public interface ISalaryCalculator {
public double calculate(Position position);
}
3.编写职位枚举Position
package org.liky.junit.easymock;
public enum Position {
BOSS, MANAGER, PROGRAMMER
}
4. 编写工资计算方法 ISalaryCalculator的实现类
package org.liky.junit.easymock;
public class SalaryCalculator implements ISalaryCalculator {
public double calculate(Position position) {
double salary = 0;
switch (position) {
case BOSS:
salary = 10000;
break;
case MANAGER:
salary = 8000;
break;
case PROGRAMMER:
salary = 6000;
break;
default:
throw new RuntimeException("No position for employee!");
}
return salary;
}
}
5.编写工资计算的类 IncomeCalculator
package org.liky.junit.easymock;
public class IncomeCalculator {
private ISalaryCalculator salaryCalculator;
private Position position;
public void setSalaryCalculator(ISalaryCalculator salaryCalculator) {
this.salaryCalculator = salaryCalculator;
}
public void setPosition(Position position) {
this.position = position;
}
public double calculate() {
if (salaryCalculator == null) {
throw new SalaryCalculatorException("Sorry, Salary Calculator is null!");
}
if (position == null) {
throw new PositionException("Sorry, Salary Calculator is null!");
}
return salaryCalculator.calculate(position);
}
}
6. 自定义异常 RuntimeException
package org.liky.junit.easymock;
public class PositionException extends RuntimeException {
private static final long serialVersionUID = -4344038596220235806L;
public PositionException() {
super();
}
public PositionException(String message) {
super(message);
}
}
7.自定义异常 SalaryCalculatorException
package org.liky.junit.easymock;
public class SalaryCalculatorException extends RuntimeException {
private static final long serialVersionUID = 4179482632698964011L;
public SalaryCalculatorException() {
super();
}
public SalaryCalculatorException(String message) {
super(message);
}
}
8.使用EasyMock与Junit4进行单元测试
package org.junit.easymock;
import static org.junit.Assert.fail;
import org.easymock.EasyMock;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.liky.junit.easymock.ISalaryCalculator;
import org.liky.junit.easymock.IncomeCalculator;
import org.liky.junit.easymock.Position;
import org.liky.junit.easymock.PositionException;
/**
* 从EasyMock的工作原理可以看出,我们在设计程序的时候,
* 应该尽量的将业务逻辑设计成接口,然后再提供实现
* @author Abu
*
*/
public class IncomeCalculatorTest {
private ISalaryCalculator sc;
private IncomeCalculator ic;
@Before
public void setUp() throws Exception {
// 根据业务逻辑接口,使用EasyMock创建一个模拟对象
sc = EasyMock.createMock(ISalaryCalculator.class);
ic = new IncomeCalculator();
}
@After
public void tearDown() throws Exception {
}
@Test
@Ignore
public void testSetSalaryCalculator() {
fail("Not yet implemented");
}
@Test
@Ignore
public void testSetPosition() {
fail("Not yet implemented");
}
/**
* 模拟职员类型为BOSS的情形,另外两种职员类型的测试与此相同,
* 注意我们在这里并没有构造实现了ISalaryCalculator接口的
* SalaryCalculator类的对象,而是通过EasyMock来模拟的
*/
@Test
public void testCalculate() {
// 设置模拟对象的参数,即期望值
EasyMock.expect(sc.calculate(Position.BOSS)).andReturn(10000d);
// 激活模拟
EasyMock.replay(sc);
// 设置计算方法为EasyMock的模拟对象,这就是它的核心功能
ic.setSalaryCalculator(sc);
ic.setPosition(Position.BOSS);
// 开始最终的断言
Assert.assertEquals(10000, ic.calculate());
// 验证模拟对象的方法是否已经执行
EasyMock.verify(sc);
}
/**
* 测试如果职员类型不是Position枚举中所列
* 抛出异常的情形
*/
@Test(expected=PositionException.class)
public void testCalculate2() {
// 设置模拟对象的参数,即期望值
EasyMock.expect(sc.calculate(Position.BOSS)).andReturn(10000d);
// 激活模拟
EasyMock.replay(sc);
// 设置计算方法为EasyMock的模拟对象,这就是它的核心功能
ic.setSalaryCalculator(sc);
// 注释掉这一样,就没有指定职员类型
//ic.setPosition(Position.BOSS);
// 开始计算薪水,如果正常的话,这行代码将抛出PositionException
double salary = ic.calculate();
// 所以如果程序继续执行将导致失败
Assert.fail("Sorry, no PositionException was thrown!");
}
}