这一段时间公司的项目进行分模块分层进行专人维护开发,所以就会有不同的service和dao有不同的人来开发,这里我们假设service和dao不同的人开发,service是依赖dao的,如果我们的dao开发人员比较忙并没有把dao模块开发好,service如果要对自己的模块进行测试该怎么做呢?这个时候我们的Easy Mock Test就可以派上用场了。
首先开发service的和dao的会讨论商量出来一套接口,假设dao的接口如下:

package cn.bridgeli.dao;

import cn.bridgeli.model.User;

public interface UserDao {  
  public User getUserById(int id);  
  public User getUserByUsername(String username);  
  //...  
}

dao模块的小朋友把它打成一个jar包,扔给service开发人员,然后我们亲爱的service开发人员就自己玩去了,最后我们的service开发人员完成了自己的任务,写下了如下的代码,当然这只是一个demo而已:

package cn.bridgeli.service.impl;

import cn.bridgeli.dao.UserDao;  
import cn.bridgeli.model.User;  
import cn.bridgeli.service.UserService;

public class UserServiceImpl implements UserService {

  private final UserDao userDao;

  public void setUserDao(UserDao userDao) {  
    this.userDao = userDao;  
  }

  @Override  
  public boolean login(String username, String password) {  
    User user = userDao.getUserByUsername(username);  
    System.err.out.println("id==" + user.getId());  
    if (user != null) {  
    String passwordInDao = user.getPassword();  
    if (passwordInDao != null && passwordInDao.equalsIgnoreCase(password)) {  
      return true;  
    }  
    return false;  
  }

  @Override  
  public User getUserById(int userId) {  
    return userDao.getUserById(userId);
  }
}

但是我们的service怎么知道自己写的有没有问题呢?现在我们的service想对自己的模块进行测试,但dao开发人员还没开始,这肯定是没办法开始的,那么怎么办呢?很简单,只需要这么做就可以了:

package cn.bridgeli.service;

import org.easymock.EasyMock;  
import org.junit.Assert;  
import org.junit.Test;

import cn.bridgeli.dao.UserDao;  
import cn.bridgeli.model.User;  
import cn.bridgeli.service.impl.UserServiceImpl;

public class UserServiceTest {

  //@Test(expected = RuntimeException.class)  
  @Test  
  public void testLogin() {  
    String userName = "bridgeli";  
    String password = "abc123_";

    //1、创建mock对象,以接口形式创建  
    UserDao userDao= EasyMock.createMock(UserDao.class);

    //2、设定参预期和返回,查询预期值得到所设定的预期结果  
    User user = new User();  
    user.setId(1);  
    user.setUserName("bridgeli");  
    user.setPassword("abc123_");  
    //...
    EasyMock.expect(userDao.getUserByUsername("bridgeli")).andReturn(user).times(1);  
    // userDao.getUserById(1);  
    EasyMock.expectLastCall().andReturn(user);  
    //...  
    userDao.getUserByUserName(userName);    
    EasyMock.expectLastCall();

    //3、结束录制  
    EasyMock.replay(userDao);

    UserServiceImpl userService = new UserServiceImpl();  
    ((UserServiceImpl)userService).setUserDao(userDao);

    boolean loginResult = userService.login(userName, password);  
    Assert.assertTrue(loginResult);

    User userIdDao = userService.getUserById(1);  
    Assert.assertNotNull(userIdDao);

    //4、回放录制  
    EasyMock.verify(userDao);
  }  
}

这就要求我们引入EasyMock的类库,pom文件如下:

<dependency>  
  <groupId>org.easymock</groupId>  
  <artifactId>easymock</artifactId>  
  <version>3.4</version>  
</dependency>

现在我们直接跑这个test是可以直接跑的,需要说明的是:

  1. EasyMock.expect(userDao.getUserByUsername(“bridgeli”))
    .andReturn(user).times(1) 是指我们要调用dao中的getUserByUsername时,返回user对象。
  2. times以为调用的次数,默认就是1,否则调几次就是写几。
  3. 我们给service和传的参数是bridgeli,所以返回的user对象和我们定义的一样,测试通过,但如果我们传的参数是其他的肯定就不行了,那么如果我们不希望是固定参数呢?这时候我们可以这么做,把:
EasyMock.expect(userDao.getUserByUsername("bridgeli")).andReturn(user).times(1)

改成:

EasyMock.expect(userDao.getUserByUsername(EasyMock.isA(String.class))).andReturn(user).times(1)

就好了,除此之外还有一些常用的方法:

anyInt(),anyObject(),isNull(),same(),startsWith()等等

  1. 期待方法返回异常,可以这么做(面向异常编程):
EasyMock.expect(userDao.getUserByUsername("bridgeli")).andThrow(new RuntimeException()).times(1)

具体还有很多方法,但因为这是一个入门教程,所以师傅领进门,修行在个人,就留给读者大胆的去测试EasyMock里面的各种各样的方法了。

最后简单说一下Mock测试的使用场景:

  1. 真实对象具有不可确定的行为(产生不可预测的结果,如股票行为,如果某种情况发生的概率极其小,可以导致无法测试);
  2. 真实对象很难被创建(如request和response对象,和容器相关,我们不能自己new,这个时候就可以mock);
  3. 真实对象的某些行为很难触发(如网络错误);
  4. 真实对象令程序的运行速度很慢;
  5. 真实对象有(或者是)用户界面;
  6. 测试需要询问真实对象他是如何被调用的(例如:测试可能需要验证某个回调函数是否被调用了);
  7. 等等,读者可以去网上自己找一下mock测试的资料。