技术开发 频道

EasyMock使单元测试更加容易

    下一步,我们来测试当用户名和口令匹配时,LoginServlet应当把请求转发给指定的RequestDispatcher。在这个测试用例中,我们除了需要HttpServletRequest Mock对象外,还需要模拟ServletContext和RequestDispatcher对象:

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对象指定的行为:

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()方法:

LoginServlet servlet = new LoginServlet() {

public ServletContext getServletContext() {

return contextObj;

}

};

servlet.doPost(requestObj, null);

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

requestCtrl.verify();

contextCtrl.verify();

dispatcherCtrl.verify();

    运行JUnit,测试通过!

    倘若LoginServlet的代码有误,例如,将context.getNamedDispatcher("dispatcher")误写为 context.getNamedDispatcher("dispatcher2"),则测试失败,JUnit报告:

junit.framework.AssertionFailedError:

Unexpected method call getNamedDispatcher("dispatcher2"):

getNamedDispatcher("dispatcher2"): expected: 0, actual: 1

getNamedDispatcher("dispatcher"): expected: 1, actual: 0

at ...

    完整的LoginServletTest代码如下:

/**

* LoginServletTest.java

* Author: Liao Xue Feng, http://www.crackj2ee.com/

*/

package com.crackj2ee.test.mock;

import javax.servlet.*;

import javax.servlet.http.*;

import org.easymock.*;

import junit.framework.TestCase;

public class LoginServletTest extends TestCase {

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();

}

public void testLoginOK() throws Exception {

// create mock:

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();

// set behavior:

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);

// done!

requestCtrl.replay();

contextCtrl.replay();

dispatcherCtrl.replay();

// test:

LoginServlet servlet = new LoginServlet() {

public ServletContext getServletContext() {

return contextObj;

}

};

servlet.doPost(requestObj, null);

// verify:

requestCtrl.verify();

contextCtrl.verify();

dispatcherCtrl.verify();

}

}

    总结:

    虽然EasyMock可以用来模仿依赖对象,但是,它只能动态模仿接口,无法模仿具体类。这一限制正好要求我们遵循“针对接口编程”的原则:如果不针对接口,则测试难于进行。应当把单元测试看作是运行时代码的最好运用,如果代码在单元测试中难于应用,则它在真实环境中也将难于应用。总之,创建尽可能容易测试的代码就是创建高质量的代码。

0
相关文章