Jmock 是一个开源的工具,建立在junit,mock object项目之上,是一个非常优秀的测试工具。
基本功能是模拟接口的实现(也可以模拟类),返回期望结果。适合测试驱动开发(TDD)。
模拟接口实现或者类的abstract方法,当然如果是类的方法访问代价很高,用mock方法代替掉在测试中也是很好的主意。
jmock的一个基础框架是junit,写Jmock测试程序其实就是写junit 套件。
对于接口的模拟,测试类需要继承 org.jmock.MockObjectTestCase。
步骤:
Mock tobeMock=new Mock(xx.class);
testBean.setXxx(tobeMock);
tobeMock.expects(once()).method("mockedMethod").withAnyArguments().will(returnValue(returnObj));
testBean.doTestMethod();
因为Jmock的用途是模拟一个和待测试的方法无关的类或者本类方法,理解这点,上面的程序还是比较容易理解的。
xx类就是我们要mock的类,testBean是待测试类,mockedMethod就是待测试的方法名称。
倒数第二行的意思就是执行xx.mockedMethod方法,只执行一次,没有参数(如果有参数,并且参数不变,可以用eq,否则可以用isA,arrayContaining等),执行后返回结果returnObj,
下面的testBean.doTestMethod方法肯定调用了xx.mockedMethod方法。这样一来,测试就能专注于testBean..doTestMethod方法,适合于TDD开发模型(Test Directive development).
一个例子:
pojo类
public class User
{
private String name;
public User()
{
}
public User(String name)
{
this.name = name;
}
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
}
DAO接口
public interface UserDAO {
public void saveUser(User user);
public User getUser(long id);
}
dao实现类
public class UserDAOImpl
{
public void saveUser(User user) throws Exception
{
throw new Exception("Not implemented");
}
public User getUser(long id)
{
return null;
}
}
public interface UserService {
public void setUserDAO(UserDAO userDAO);
public void saveUser(User user);
public User getUser(long l);
}
接口实现类
public class UserServiceImpl implements UserService {
private UserDAO userDAO;
public UserServiceImpl() {
}
public void setUserDAO(UserDAO userDAO) {
this.userDAO = userDAO;
}
public void saveUser(User user) {
userDAO.saveUser(user);
}
public User getUser(long id)
{
return userDAO.getUser(id);
}
}
现在开始写测试类
import com.sarkuya.model.User;
import junit.framework.*;
import org.jmock.Mock;
import org.jmock.MockObjectTestCase;
public class UserServiceTest extends MockObjectTestCase {
private UserService userService = new UserServiceImpl();
private Mock userDAO = null;
public UserServiceTest(String testName) {
super(testName);
}
//初始化
protected void setUp() throws Exception {
userDAO = new Mock(UserDAO.class);
userService.setUserDAO((UserDAO)userDAO.proxy());
}
protected void tearDown() throws Exception {
userDAO = null;
userService=null;
}
public static Test suite() {
TestSuite suite = new TestSuite(UserServiceTest.class);
return suite;
}
public void testGetUser() {
User fakeUser = new User("John");
userDAO.expects(once()).method("getUser").with(eq(1L)).will(returnValue(fakeUser));
// userDAO.expects(atLeastOnce()).method("getUser").with(eq(1L))
// .will( onConsecutiveCalls(
// returnValue(fakeUser),
// returnValue(20),
// throwException(new IOException("end of stream")) ) );
//
User user = userService.getUser(1L);
assertNotNull(user);
assertEquals("John", user.getName());
}
public void testSaveUser() {
User fakeUser = new User("John");
userDAO.expects(this.once()).method("getUser").with(eq(1L)).will(returnValue(fakeUser));
User user = userService.getUser(1L);
assertEquals("John", user.getName());
userDAO.expects(once()).method("saveUser").with(same(fakeUser));
user.setName("Mike");
userService.saveUser(user);
userDAO.expects(once()).method("getUser").with(eq(1L)).will(returnValue(user));
User modifiedUser = userService.getUser(1L);
assertEquals("Mike", user.getName());
}
}
现在第一个jmock类完成。
对于非接口的模拟,需要继承org.jmock.cglib.MockObjectTestCase类,同时新建Mock类的时候需要把变量名作为参数。
修改上面的类为Service2,其他不变
public class Service2
{
private UserDAOImpl userDAO;
public void setUserDAO(UserDAOImpl userDAO) {
this.userDAO = userDAO;
}
public void saveUser(User user) throws Exception {
userDAO.saveUser(user);
}
public User getUser(long id)
{
return userDAO.getUser(id);
}
}
测试类如下
import junit.framework.Test;
import junit.framework.TestSuite;
import org.jmock.Mock;
import org.jmock.cglib.MockObjectTestCase;
public class Service2Test extends MockObjectTestCase
{
private Service2 userService = new Service2();
private Mock userDAO = null;
public Service2Test(String testName)
{
super(testName);
}
protected void setUp() throws Exception
{
//注意
userDAO = mock(UserDAOImpl.class, "userDAO");
userService.setUserDAO((UserDAOImpl) userDAO.proxy());
}
protected void tearDown() throws Exception
{
}
public static Test suite()
{
TestSuite suite = new TestSuite(Service2Test.class);
return suite;
}
public void testGetUser()
{
User fakeUser = new User("John");
userDAO.expects(once()).method("getUser").with(eq(1L)).will(returnValue(fakeUser));
User user = userService.getUser(1L);
assertNotNull(user);
assertEquals("John", user.getName());
}
public void testSaveUser()
{
User fakeUser = new User("John");
userDAO.expects(once()).method("getUser").with(eq(1L)).will(returnValue(fakeUser));
User user = userService.getUser(1L);
assertEquals("John", user.getName());
userDAO.expects(once()).method("saveUser").with(same(fakeUser));
user.setName("Mike");
try {
userService.saveUser(user);
} catch (Exception e) {
e.printStackTrace();
}
userDAO.expects(once()).method("getUser").with(eq(1L)).will(returnValue(user));
User modifiedUser = userService.getUser(1L);
assertEquals("Mike", user.getName());
}
}
对于类的Mock,如果有代参数的构造方法,需要把参数和参数类型作为参数,分别和成一个数组。在mock方法中加上就可以了。
如下:
Class [] pClass = {String.class, String.class};
String pass=...,name=...;
Object [] pObject = {pass,name};
userDAO = mock(UserDAOImpl.class, "userDAO",pClass,pObject);