现在的位置: 首页 > 综合 > 正文

看了抛哥的mock留言,找了找easymock,留个笔记,分享给大家!

2013年06月21日 ⁄ 综合 ⁄ 共 11101字 ⁄ 字号 评论关闭

 

看了抛哥的mock留言,找了找easymock,留个笔记,分享给大家!

    博客分类:

  • J2SE

单元测试是XP 极力推荐的测试驱动开发模式,是保证软件质量的重要方法。尽管如此,对许多

类的单元测试仍然是极其困难的,例如,对数据库操作的类进行测试,如果不准备好数据库环境以

及相关测试数据,是很难进行单元测试的;再例如,对需要运行在容器内的Servlet 或EJB 组件,脱

离了容器也难于测试。

幸运的是,Mock Object 可以用来模拟一些我们需要的类,这些对象被称之为模仿对象,在单元

测试中它们特别有价值。

Mock Object 用于模仿真实对象的方法调用,从而使得测试不需要真正的依赖对象。Mock Object

只为某个特定的测试用例的场景提供刚好满足需要的最少功能。它们还可以模拟错误的条件,例如

抛出指定的异常等。

目前,有许多可用的Mock 类库可供我们选择。一些Mock 库提供了常见的模仿对象,例如:

HttpServletRequest,而另一些Mock 库则提供了动态生成模仿对象的功能,本文将讨论使用EasyMock

动态生成模仿对象以便应用于单元测试。

到目前为止,EasyMock 提供了1.2 版本和2.0 版本,2.0 版本仅支持Java SE 5.0,本例中,我们

选择EasyMock 1.2 for Java 1.3 版本进行测试,可以从http://www.easymock.org 下载合适的版本。

我们首先来看一个用户验证的LoginServlet 类:

Java代码 复制代码 收藏代码
  1. /**  
  2. * LoginServlet.java  
  3. */  
  4. package com.javaeedev.test.mock;   
  5. import java.io.*;   
  6. import javax.servlet.*;   
  7. import javax.servlet.http.*;   
  8.   
  9. public class LoginServlet extends HttpServlet {   
  10.     protected void doPost(HttpServletRequest request, HttpServletResponse response)   
  11.         throws ServletException, IOException {   
  12.             String username = request.getParameter("username");   
  13.             String password = request.getParameter("password");   
  14.             // check username & password:   
  15.             if("admin".equals(username) && "123456".equals(password)) {   
  16.                 ServletContext context = getServletContext();   
  17.                 RequestDispatcher dispatcher = context.getNamedDispatcher("dispatcher");   
  18.                 dispatcher.forward(request, response);   
  19.             }else {   
  20.                 throw new RuntimeException("Login failed.");   
  21.             }   
  22.     }   
  23. }  
/*** LoginServlet.java*/package com.javaeedev.test.mock;import java.io.*;import javax.servlet.*;import javax.servlet.http.*;public class LoginServlet extends HttpServlet {	protected void doPost(HttpServletRequest request, HttpServletResponse response)		throws ServletException, IOException {			String username = request.getParameter("username");			String password = request.getParameter("password");			// check username & password:			if("admin".equals(username) && "123456".equals(password)) {				ServletContext context = getServletContext();				RequestDispatcher dispatcher = context.getNamedDispatcher("dispatcher");				dispatcher.forward(request, response);			}else {				throw new RuntimeException("Login failed.");			}	}}

 

这个Servlet 实现简单的用户验证的功能,若用户名和口令匹配“admin”和“123456”,则请求

被转发到指定的dispatcher 上,否则,直接抛出RuntimeException。

为了测试doPost()方法,我们需要模拟HttpServletRequest,ServletContext 和RequestDispatcher

对象,以便脱离J2EE 容器来测试这个Servlet。

我们建立TestCase,名为LoginServletTest:

Java代码 复制代码 收藏代码
  1. public class LoginServletTest extends TestCase {   
  2. }  
public class LoginServletTest extends TestCase {}

 

我们首先测试当用户名和口令验证失败的情形, 演示如何使用EasyMock 来模拟

 

HttpServletRequest 对象:

Java代码 复制代码 收藏代码
  1. public void testLoginFailed() throws Exception {   
  2.     MockControl mc = MockControl.createControl(HttpServletRequest.class);   
  3.     HttpServletRequest request = (HttpServletRequest)mc.getMock();   
  4.     // set Mock Object behavior:   
  5.     request.getParameter("username");   
  6.     mc.setReturnValue("admin", 1);   
  7.     request.getParameter("password");   
  8.     mc.setReturnValue("1234", 1);   
  9.     // ok, all behaviors are set!   
  10.     mc.replay();   
  11.     // now start test:   
  12.     LoginServlet servlet = new LoginServlet();   
  13.     try {   
  14.         servlet.doPost(request, null);   
  15.         fail("Not caught exception!");   
  16.     }catch(RuntimeException re) {   
  17.         assertEquals("Login failed.", re.getMessage());   
  18.     }   
  19.     // verify:   
  20.     mc.verify();   
  21. }  
public void testLoginFailed() throws Exception {	MockControl mc = MockControl.createControl(HttpServletRequest.class);	HttpServletRequest request = (HttpServletRequest)mc.getMock();	// set Mock Object behavior:	request.getParameter("username");	mc.setReturnValue("admin", 1);	request.getParameter("password");	mc.setReturnValue("1234", 1);	// ok, all behaviors are set!	mc.replay();	// now start test:	LoginServlet servlet = new LoginServlet();	try {		servlet.doPost(request, null);		fail("Not caught exception!");	}catch(RuntimeException re) {		assertEquals("Login failed.", re.getMessage());	}	// verify:	mc.verify();}

 

 

仔细观察测试代码,使用EasyMock 来创建一个Mock 对象需要首先创建一个MockControl:

Java代码 复制代码 收藏代码
  1. MockControl mc = MockControl.createControl(HttpServletRequest.class);  
MockControl mc = MockControl.createControl(HttpServletRequest.class);

 

 

然后,即可获得MockControl 创建的Mock 对象:

Java代码 复制代码 收藏代码
  1. HttpServletRequest request = (HttpServletRequest)mc.getMock();  
HttpServletRequest request = (HttpServletRequest)mc.getMock();

 

下一步,我们需要“录制”Mock 对象的预期行为。在LoginServlet 中, 先后调用了

request.getParameter("username") 和request.getParameter("password") 两个方法, 因此, 需要在

MockControl 中设置这两次调用后的指定返回值。我们期望返回的值为“admin”和“1234”:

Java代码 复制代码 收藏代码
  1. request.getParameter("username"); // 期望下面的测试将调用此方法,参数为"username"   
  2. mc.setReturnValue("admin", 1); // 期望返回值为"admin",仅调用1 次   
  3. request.getParameter("password"); // 期望下面的测试将调用此方法,参数为" password"   
  4. mc.setReturnValue("1234", 1); // 期望返回值为"1234",仅调用1 次  
request.getParameter("username"); // 期望下面的测试将调用此方法,参数为"username"mc.setReturnValue("admin", 1); // 期望返回值为"admin",仅调用1 次request.getParameter("password"); // 期望下面的测试将调用此方法,参数为" password"mc.setReturnValue("1234", 1); // 期望返回值为"1234",仅调用1 次

 

紧接着,调用mc.replay(),表示Mock 对象“录制”完毕,可以开始按照我们设定的方式运行,

我们对LoginServlet 进行测试,并预期会产生一个RuntimeException:

 

 

Java代码 复制代码 收藏代码
  1. LoginServlet servlet = new LoginServlet();   
  2. try {   
  3.     servlet.doPost(request, null);   
  4.     fail("Not caught exception!");   
  5. }catch(RuntimeException re) {   
  6.     assertEquals("Login failed.", re.getMessage());   
  7. }  
LoginServlet servlet = new LoginServlet();try {	servlet.doPost(request, null);	fail("Not caught exception!");}catch(RuntimeException re) {	assertEquals("Login failed.", re.getMessage());}

 

由于本次测试的目的是检查当用户名和口令验证失败后, LoginServlet 是否会抛出

RuntimeException,因此,response 对象对测试没有影响,我们不需要模拟它,仅仅传入null 即可。

最后,调用mc.verify()检查Mock 对象是否按照预期的方法调用正常运行了。

运行JUnit,测试通过!表示我们的Mock 对象正确工作了!

下一步,我们来测试当用户名和口令匹配时,LoginServlet 应当把请求转发给指定的

RequestDispatcher。在这个测试用例中,我们除了需要HttpServletRequest Mock 对象外,还需要模拟

ServletContext 和RequestDispatcher 对象:

 

 

Java代码 复制代码 收藏代码
  1. MockControl requestCtrl = MockControl.createControl(HttpServletRequest.class);   
  2. HttpServletRequest requestObj = (HttpServletRequest)requestCtrl.getMock();   
  3. MockControl contextCtrl = MockControl.createControl(ServletContext.class);   
  4. final ServletContext contextObj = (ServletContext)contextCtrl.getMock();   
  5. MockControl dispatcherCtrl =MockControl.createControl(RequestDispatcher.class);   
  6. RequestDispatcher dispatcherObj = (RequestDispatcher)dispatcherCtrl.getMock();  
MockControl requestCtrl = MockControl.createControl(HttpServletRequest.class);HttpServletRequest requestObj = (HttpServletRequest)requestCtrl.getMock();MockControl contextCtrl = MockControl.createControl(ServletContext.class);final ServletContext contextObj = (ServletContext)contextCtrl.getMock();MockControl dispatcherCtrl =MockControl.createControl(RequestDispatcher.class);RequestDispatcher dispatcherObj = (RequestDispatcher)dispatcherCtrl.getMock();

 

按照doPost()的语句顺序,我们设定Mock 对象指定的行为:

Java代码 复制代码 收藏代码
  1. requestObj.getParameter("username");   
  2. requestCtrl.setReturnValue("admin", 1);   
  3. requestObj.getParameter("password");   
  4. requestCtrl.setReturnValue("123456", 1);   
  5. contextObj.getNamedDispatcher("dispatcher");   
  6. contextCtrl.setReturnValue(dispatcherObj, 1);   
  7. dispatcherObj.forward(requestObj, null);   
  8. dispatcherCtrl.setVoidCallable(1);   
  9. requestCtrl.replay();   
  10. contextCtrl.replay();   
  11. dispatcherCtrl.replay();  
requestObj.getParameter("username");requestCtrl.setReturnValue("admin", 1);requestObj.getParameter("password");requestCtrl.setReturnValue("123456", 1);contextObj.getNamedDispatcher("dispatcher");contextCtrl.setReturnValue(dispatcherObj, 1);dispatcherObj.forward(requestObj, null);dispatcherCtrl.setVoidCallable(1);requestCtrl.replay();contextCtrl.replay();dispatcherCtrl.replay();

 

然后,测试doPost()方法,这里,为了让getServletContext()方法返回我们创建的ServletContext

Mock 对象,我们定义一个匿名类并覆写getServletContext()方法:

Java代码 复制代码 收藏代码
  1. LoginServlet servlet = new LoginServlet() {   
  2.     public ServletContext getServletContext() {   
  3.         return contextObj;   
  4.     }   
  5. };   
  6. servlet.doPost(requestObj, null);  
LoginServlet servlet = new LoginServlet() {	public ServletContext getServletContext() {		return contextObj;	}};servlet.doPost(requestObj, null);

 

最后,检查所有Mock 对象的状态:

 

 

Java代码 复制代码 收藏代码
  1. requestCtrl.verify();   
  2. contextCtrl.verify();   
  3. dispatcherCtrl.verify();  
requestCtrl.verify();contextCtrl.verify();dispatcherCtrl.verify();

运行JUnit,测试通过!

倘若LoginServlet 的代码有误, 例如, 将context.getNamedDispatcher("dispatcher") 误写为

context.getNamedDispatcher("dispatcher2"),则测试失败,JUnit 报告:

Java代码 复制代码 收藏代码
  1. junit.framework.AssertionFailedError:   
  2. Unexpected method call getNamedDispatcher("dispatcher2"):   
  3. getNamedDispatcher("dispatcher2"): expected: 0, actual: 1  
  4. getNamedDispatcher("dispatcher"): expected: 1, actual: 0  
  5. at ...  
junit.framework.AssertionFailedError:Unexpected method call getNamedDispatcher("dispatcher2"):getNamedDispatcher("dispatcher2"): expected: 0, actual: 1getNamedDispatcher("dispatcher"): expected: 1, actual: 0at ...

 

完整的LoginServletTest 代码如下:

Java代码 复制代码 收藏代码
  1. /**  
  2.  * LoginServletTest.java  
  3.  */  
  4. package com.javaeedev.test.mock;   
  5.   
  6. import javax.servlet.*;   
  7. import javax.servlet.http.*;   
  8. import org.easymock.*;   
  9. import junit.framework.TestCase;   
  10.   
  11. public class LoginServletTest extends TestCase {   
  12.     public void testLoginFailed() throws Exception {   
  13.         MockControl mc = MockControl.createControl(HttpServletRequest.class);   
  14.         HttpServletRequest request = (HttpServletRequest)mc.getMock();   
  15.         // set Mock Object behavior:   
  16.         request.getParameter("username");   
  17.         mc.setReturnValue("admin", 1);   
  18.         request.getParameter("password");   
  19.         mc.setReturnValue("1234", 1);   
  20.         // ok, all behaviors are set!   
  21.         mc.replay();   
  22.         // now start test:   
  23.         LoginServlet servlet = new LoginServlet();   
  24.         try {   
  25.             servlet.doPost(request, null);   
  26.             fail("Not caught exception!");   
  27.         }catch(RuntimeException re) {   
  28.             assertEquals("Login failed.", re.getMessage());   
  29.         }   
  30.         // verify:   
  31.         mc.verify();   
  32.     }   
  33.     public void testLoginOK() throws Exception {   
  34.         // create mock:   
  35.         MockControl requestCtrl = MockControl.createControl(HttpServletRequest.class);   
  36.         HttpServletRequest requestObj = (HttpServletRequest)requestCtrl.getMock();   
  37.         MockControl contextCtrl = MockControl.createControl(ServletContext.class);   
  38.         final ServletContext contextObj = (ServletContext)contextCtrl.getMock();   
  39.         MockControl dispatcherCtrl =   
  40.         MockControl.createControl(RequestDispatcher.class);   
  41.         RequestDispatcher dispatcherObj = (RequestDispatcher)dispatcherCtrl.getMock();   
  42.         // set behavior:   
  43.         requestObj.getParameter("username");   
  44.         requestCtrl.setReturnValue("admin", 1);   
  45.         requestObj.getParameter("password");   
  46.         requestCtrl.setReturnValue("123456", 1);   
  47.         contextObj.getNamedDispatcher("dispatcher");   
  48.         contextCtrl.setReturnValue(dispatcherObj, 1);   
  49.         dispatcherObj.forward(requestObj, null);   
  50.         dispatcherCtrl.setVoidCallable(1);   
  51.         // done!   
  52.         requestCtrl.replay();   
  53.         contextCtrl.replay();   
  54.         dispatcherCtrl.replay();   
  55.         // test:   
  56.         LoginServlet servlet = new LoginServlet() {   
  57.             public ServletContext getServletContext() {   
  58.                 return contextObj;   
  59.             }   
  60.         };   
  61.         servlet.doPost(requestObj, null);   
  62.         // verify:   
  63.         requestCtrl.verify();   
  64.         contextCtrl.verify();   
  65.         dispatcherCtrl.verify();   
  66.     }   
  67. }  

抱歉!评论已关闭.