renxiao2003 发表于 2011-12-21 08:44

给servlet写单元测试的总结

<DIV>servlet的测试一般来说需要容器的支持,不是像通常的java类的junit测试一样简单,<BR>&nbsp;<BR>下面通过对HelloWorld代码的测试阐述了几种servlet测试方法。<BR>&nbsp;<BR>被测试的HelloWorld类的代码如下:<BR>&nbsp;<BR>/**<BR>&nbsp;* 被测试的servlet<BR>&nbsp;*/</DIV>
<DIV>import java.io.IOException;<BR>&nbsp;<BR>import javax.servlet.http.HttpServlet;<BR>import javax.servlet.http.HttpServletRequest;<BR>import javax.servlet.http.HttpServletResponse;<BR>import org.apache.cactus.WebRequest; <BR>import org.apache.cactus.server.HttpServletRequestWrapper;<BR>&nbsp;<BR>public class HelloWorld extends HttpServlet{<BR>&nbsp;<BR>&nbsp;public void saveToSession(HttpServletRequest request) {</DIV>
<DIV>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; request.getSession().setAttribute("testAttribute",request.getParameter("testparam"));</DIV>
<DIV>&nbsp;}<BR>&nbsp;<BR>&nbsp;public void doGet(HttpServletRequest request,HttpServletResponse response) throws IOException{</DIV>
<DIV>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String username=request.getParameter("username");</DIV>
<DIV>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; response.getWriter().write(username+":Hello World!");<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;}<BR>&nbsp;<BR>&nbsp;public boolean authenticate(){<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return true;<BR>&nbsp;<BR>&nbsp;}</DIV>
<DIV>}<BR>&nbsp;<BR>以HelloWorld为例,我总结了Servlet的多种测试方法如下:<BR>&nbsp;<BR>一.使用HttpUnit测试<BR>&nbsp;<BR>import com.meterware.httpunit.GetMethodWebRequest;<BR>import com.meterware.httpunit.WebRequest;<BR>import com.meterware.httpunit.WebResponse;<BR>import com.meterware.servletunit.InvocationContext;<BR>import com.meterware.servletunit.ServletRunner;<BR>import com.meterware.servletunit.ServletUnitClient;<BR>import junit.framework.Assert;<BR>import junit.framework.TestCase;<BR>&nbsp;<BR>public class HttpUnitTestHelloWorld extends TestCase {<BR>&nbsp;<BR>&nbsp;protected void setUp() throws Exception {<BR>&nbsp; super.setUp();<BR>&nbsp;}<BR>&nbsp;<BR>&nbsp;protected void tearDown() throws Exception {<BR>&nbsp; super.tearDown();<BR>&nbsp;}<BR>&nbsp;<BR>&nbsp;public void testHelloWorld() {<BR>&nbsp; <BR>&nbsp; try {</DIV>
<DIV>&nbsp;&nbsp; // 创建Servlet的运行环境</DIV>
<DIV>&nbsp;&nbsp; ServletRunner sr = new ServletRunner();</DIV>
<DIV>&nbsp;&nbsp; // 向环境中注册Servlet</DIV>
<DIV>&nbsp;&nbsp; sr.registerServlet("HelloWorld", HelloWorld.class.getName());<BR>&nbsp;<BR>&nbsp;&nbsp; // 创建访问Servlet的客户端</DIV>
<DIV>&nbsp;&nbsp; ServletUnitClient sc = sr.newClient();</DIV>
<DIV>&nbsp;&nbsp; // 发送请求</DIV>
<DIV>&nbsp;&nbsp; WebRequest request = new GetMethodWebRequest("<a href="http://localhost/HelloWorld" target="_blank">http://localhost/HelloWorld</A>");<BR>&nbsp;&nbsp; request.setParameter("username", "testuser");</DIV>
<DIV>&nbsp;&nbsp; InvocationContext ic = sc.newInvocation(request);</DIV>
<DIV>&nbsp;&nbsp; HelloWorld is = (HelloWorld) ic.getServlet();<BR>&nbsp;<BR>&nbsp;&nbsp; // 测试servlet的某个方法</DIV>
<DIV>&nbsp;&nbsp; Assert.assertTrue(is.authenticate());</DIV>
<DIV>&nbsp;&nbsp; // 获得模拟服务器的信息</DIV>
<DIV>&nbsp;&nbsp; WebResponse response = sc.getResponse(request);</DIV>
<DIV>&nbsp;&nbsp; // 断言</DIV>
<DIV>&nbsp;&nbsp; Assert.assertTrue(response.getText().equals("testuser:Hello World!"));</DIV>
<DIV>&nbsp; } catch (Exception e) {</DIV>
<DIV>&nbsp;&nbsp; e.printStackTrace();</DIV>
<DIV>&nbsp; }</DIV>
<DIV>&nbsp;}<BR>&nbsp;<BR>}<BR>&nbsp;<BR>上述例子其实是junit的一个测试例子,在其中使用了httpunit模拟的servlet环境,使用上述方法测试<BR>&nbsp;<BR>servlet可以脱离容器,容易把该测试写入ant或maven脚本,让测试进行。<BR>&nbsp;<BR>httpunit网址:<a href="http://httpunit.sourceforge.net/" target="_blank">http://httpunit.sourceforge.net/</A><BR>&nbsp;<BR>使用该种方法测试的弱点就是:如果要使用request(response)的setCharercterEncoding方法时,测试会出现一些问题,<BR>&nbsp;<BR>而且httpunit在测试servlet行为时,采用的是完全模拟浏览器,有时测试比较难写。<BR>&nbsp;<BR>二 使用cactus测试<BR>&nbsp;<BR>/**<BR>&nbsp;* cactus测试servlet的例子<BR>&nbsp;* 必须要有tomcat的支持<BR>&nbsp;* <BR>&nbsp;*/<BR>&nbsp;<BR>import junit.framework.Test;<BR>import junit.framework.TestSuite;<BR>import org.apache.cactus.ServletTestCase; <BR>import org.apache.cactus.WebRequest;<BR>import org.apache.cactus.WebResponse;<BR>public class CactusHelloWorld extends ServletTestCase{<BR>&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp; HelloWorld servlet;<BR>&nbsp;&nbsp;&nbsp;&nbsp; public CactusHelloWorld(String theName) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; super(theName);<BR>&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp; protected void setUp() throws Exception {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; super.setUp();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; servlet = new HelloWorld();<BR>&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp; protected void tearDown() throws Exception {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; super.tearDown();<BR>&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp; /**<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * 测试方法测试参数在此设置<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * @param webrequest<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; */<BR>&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp; public void beginSaveToSessionOK(WebRequest request) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; request.addParameter("testparam", "it works!");<BR>&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp; /**<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * 测试方法测试参数在此设置<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * @param webrequest<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; */<BR>&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp; public void beginDoGet(WebRequest request) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; request.addParameter("username", "testuser");<BR>&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp; /**<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * 调用servlet的测试方法<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; *&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; */</DIV>
<DIV>&nbsp;&nbsp;&nbsp;&nbsp; public void testSaveToSessionOK() {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; servlet.saveToSession(request);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; assertEquals("it works!", session.getAttribute("testAttribute"));<BR>&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp; public void testDoGet() {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; try {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; servlet.doGet(request, response);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } catch (Exception e) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; e.printStackTrace();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp; /**<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * 此方法可以判断测试方法的输出,会传递测试方法的reponse给end***,并且格式化为cactus<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * 的WebResponse或者可以跟httpunit集成,格式化为httpunit的response<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * @param response<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; */</DIV>
<DIV>&nbsp;&nbsp;&nbsp;&nbsp; public void endDoGet(WebResponse response) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String content;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; content = response.getText();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; assertEquals("testuser:Hello World!", content);<BR>&nbsp;&nbsp;&nbsp;&nbsp; }<BR>}<BR>&nbsp;<BR>cactus具备丰富灵活的测试功能,如要测试doGet方法,分为beginDoGet(模拟测试参数设置)、DoGet(执行测试)、endDoGet(状态结果验证)<BR>&nbsp;<BR>相比httpunit来说,写测试更为容易,测试servlet更为专业,流程更为清晰,但是cactus需要容器支持,使得测试不可以自动进行,但是<BR>&nbsp;<BR>如果使用一个嵌入式的容器,测试就可以自动了。<BR>&nbsp;<BR>cactus是一个servlet和jsp的测试框架:http://jakarta.apache.org/cactus/getting_started.html<BR>&nbsp;<BR>三 使用Jetty作为嵌入式容器测试servlet.<BR>&nbsp;<BR>/**<BR>&nbsp;* 一个关于嵌入式jetty测试的例子,jetty作为stubs的一个例子<BR>&nbsp;* <BR>&nbsp;*/<BR>package com.easyjf.testexample;<BR>&nbsp;<BR>import org.mortbay.jetty.Connector;<BR>import org.mortbay.jetty.Server;<BR>import org.mortbay.jetty.bio.SocketConnector;<BR>import org.mortbay.jetty.servlet.ServletHandler;<BR>&nbsp;<BR>import com.meterware.httpunit.WebClient;<BR>import com.meterware.httpunit.WebConversation;<BR>import com.meterware.httpunit.WebResponse;<BR>&nbsp;<BR>import junit.framework.Assert;<BR>import junit.framework.TestCase;<BR>&nbsp;<BR>public class JettySampleTest extends TestCase {<BR>&nbsp;<BR>&nbsp;Server server;<BR>&nbsp;protected void setUp() throws Exception {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //通过代码设置并启动一个服务器,该服务器是servlet的测试容器<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; super.setUp();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; server = new Server();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Connector connector=new SocketConnector();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; connector.setPort(80);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; server.setConnectors(new Connector[]{connector});<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ServletHandler handler=new ServletHandler();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; server.setHandler(handler);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; handler.addServletWithMapping("HelloWorld", "/");<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; server.start();<BR>&nbsp;}<BR>&nbsp;<BR>&nbsp;protected void tearDown() throws Exception {<BR>&nbsp; super.tearDown();<BR>&nbsp; server.stop();<BR>&nbsp;}<BR>&nbsp;<BR>&nbsp;public void testHellWorld() {<BR>&nbsp; try {<BR>&nbsp;&nbsp; WebConversation wc = new WebConversation();<BR>&nbsp;&nbsp; WebResponse web = wc.getResponse("<a href="http://127.0.0.1/HelloWorld" target="_blank">http://127.0.0.1/HelloWorld</A>");<BR>&nbsp;&nbsp; String result=web.getText();<BR>&nbsp;&nbsp; Assert.assertEquals(result,"it works!");<BR>&nbsp; } catch (Exception e) {<BR>&nbsp;&nbsp; e.printStackTrace();<BR>&nbsp; }<BR>&nbsp;}<BR>}<BR>&nbsp;<BR>可以发现,jetty可以充当一个servlet的容器,方便的是,jetty支持嵌入式服务,即可以通过代码来启动,<BR>&nbsp;<BR>所以要写自动测试的例子很方便,可以结合httpunit或者cactus进行servlet测试。<BR>&nbsp;<BR>jetty主页:<a href="http://docs.codehaus.org/display/JETTY/Embedding+Jetty" target="_blank">http://docs.codehaus.org/display/JETTY/Embedding+Jetty</A></DIV>
<DIV><BR>四 使用mock对象,此处使用easymock<BR>&nbsp;<BR>import java.io.PrintWriter;<BR>import java.io.Writer;<BR>import javax.servlet.ServletConfig;<BR>import javax.servlet.ServletContext;<BR>import javax.servlet.http.HttpServletRequest;<BR>import javax.servlet.http.HttpServletResponse;<BR>&nbsp;<BR>import junit.framework.Assert;<BR>import junit.framework.TestCase;<BR>import static org.easymock.EasyMock.*;<BR>public class MockTestServlet extends TestCase {<BR>&nbsp;<BR>&nbsp;&nbsp;&nbsp; public void testService() throws Exception {</DIV>
<DIV>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println("service");</DIV>
<DIV>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; HttpServletRequest request = createMock(HttpServletRequest.class);</DIV>
<DIV>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; HttpServletResponse response = createMock(HttpServletResponse.class);</DIV>
<DIV>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //Creating the ServletConfig mock here</DIV>
<DIV>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ServletConfig servletConfig = createMock(ServletConfig.class);</DIV>
<DIV>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //Creating the ServletContext mock here</DIV>
<DIV>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ServletContext servletContext = createMock(ServletContext.class);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //Create the target object&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; HelloWorld4 instance = new HelloWorld();</DIV>
<DIV>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //初始化servlet,一般由容器承担,一般调用servletConfig作为参数初始化,此处模拟容器行为</DIV>
<DIV>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; instance.init(servletConfig);<BR>&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //在某些方法被调用时设置期望的返回值,如下这样就不会去实际调用servletConfig的getServletContext方法,而是直接返回<BR>&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //servletContext,由于servletConfig是mock出来的,所以可以完全控制。</DIV>
<DIV>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; expect(servletConfig.getServletContext()).andReturn(servletContext).anyTimes();</DIV>
<DIV>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; expect(request.getParameter("username")).andReturn("testuser");</DIV>
<DIV>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; PrintWriter pw=new PrintWriter(System.out,true);</DIV>
<DIV>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; expect(response.getWriter()).andReturn(pw).anyTimes();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //以上均是录制,下面为重放,该种机制为easymock测试机制,要理解请看easymock测试的一些资料<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; replay(request);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; replay(response);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; replay(servletConfig);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; replay(servletContext);<BR>&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; instance.doGet(request, response);</DIV>
<DIV>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pw.flush();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //验证结果是否预期,如果预期,则会在pw上写出testuser.<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; verify(request);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; verify(response);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; verify(servletConfig);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; verify(servletContext);<BR>&nbsp;&nbsp; }<BR>}<BR>&nbsp;<BR>mock测试注重行为,mock对象其实都是模拟的对象,方法一般直接给出一个返回值,没有具体的对象逻辑,mock对象<BR>&nbsp;<BR>是用来帮助测试要测试的类的。比如要测试servlet的内部行为,又不想要容器等环境,就可以采用mock测试。<BR>&nbsp;<BR>easymock是mock测试的一个框架:<a href="http://www.easymock.org/" target="_blank">http://www.easymock.org/</A></DIV>
<DIV><BR>本文来自CSDN博客,转载请标明出处:<a href="http://blog.csdn.net/cz_hyf/archive/2007/02/10/1507211.aspx" target="_blank">http://blog.csdn.net/cz_hyf/archive/2007/02/10/1507211.aspx</A></DIV>
页: [1]
查看完整版本: 给servlet写单元测试的总结