传统的Java WEB应用中,核心技术莫过于Servlet类与JSP网页,两者均可以通过HttpUnit程序包完成单元测试。对JSP网页的测试主要集中在判断HTTP服务器返回的内容是否符合要求,并且这种测试只能在WEB容器内进行。对于Servlet类的测试,HttpUnit程序包给出了一个非容器内的测试方案,那就是ServletRunner类的使用。
简单测试
为了测试Servlet类,首先要在ServletRunner中注册Servlet类,例如:
- // 模拟WEB服务器
- ServletRunner sr = new ServletRunner();
- sr.registerServlet( "hello.html" , HelloServlet. class .getName() );
上文注册了一个HelloServlet,当程序发出“hello.html”的HTTP请求时,ServletRunner就会调用HelloServlet类予以响应,如:
- // 模拟HTTP客户端
- ServletUnitClient sc = sr.newClient();
- // 创建请求
- WebRequest request =
- new GetMethodWebRequest( "http://localhost/hello.html" );
- // 返回响应
- WebResponse response = sc.getResponse( request );
- // 校验结果
- assertEquals( "text/plain" , response.getContentType());
- assertEquals( "UTF-8" , response.getCharacterSet());
- assertEquals( "中国" , response.getText());
根据上述测试过程,我们的HelloServlet类实现如下:
- import java.io.IOException;
- import java.io.PrintWriter;
- import javax.servlet.ServletException;
- import javax.servlet.http.HttpServlet;
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
- public class HelloServlet extends HttpServlet {
- @Override
- protected void doGet(HttpServletRequest req, HttpServletResponse resp)
- throws ServletException, IOException {
- resp.setContentType( "text/plain" );
- resp.setCharacterEncoding( "UTF-8" );
- PrintWriter pw = resp.getWriter();
- pw.write( "中国" );
- }
- }
当然,我们也可以判断Servlet类操作session的过程是否正确,如:
- import junit.framework.TestCase;
- import com.meterware.httpunit.GetMethodWebRequest;
- import com.meterware.httpunit.WebRequest;
- import com.meterware.httpunit.WebResponse;
- import com.meterware.servletunit.ServletRunner;
- import com.meterware.servletunit.ServletUnitClient;
- public class HelloTest extends TestCase {
- public void testHelloServlet() throws Exception {
- ServletRunner sr = new ServletRunner();
- sr.registerServlet( "hello.html" , HelloServlet. class .getName());
- ServletUnitClient sc = sr.newClient();
- WebRequest request =
- new GetMethodWebRequest( "http://localhost/hello.html" );
- WebResponse response = sc.getResponse( request );
- // 判断session中的值
- assertEquals( "darxin" , sc.getSession( false ).getAttribute( "userId" ));
- assertEquals( "text/plain" , response.getContentType());
- assertEquals( "UTF-8" , response.getCharacterSet());
- assertEquals( "中国" , response.getText());
- }
- }
相应的,我们的Servlet类会做如下改动:
- import java.io.IOException;
- import java.io.PrintWriter;
- import javax.servlet.ServletException;
- import javax.servlet.http.HttpServlet;
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
- public class HelloServlet extends HttpServlet {
- @Override
- protected void doGet(HttpServletRequest req, HttpServletResponse resp)
- throws ServletException, IOException {
- // 向session中设置属性
- req.getSession().setAttribute( "userId" , "darxin" );
- resp.setContentType( "text/plain" );
- resp.setCharacterEncoding( "UTF-8" );
- PrintWriter pw = resp.getWriter();
- pw.write( "中国" );
- }
- }
高级应用
上述两例均属于在Servlet类中直接打印响应信息的情况,在实际应用中这种调用已经很少见了。通常我们会利用MVC架构实现Servlet类与JSP网页的功能分离。例如使用Servlet类完成Controller的任务;使用JSP网页完成View的任务。
下图展示了一个典型的利用MVC架构实现HTTP响应的过程:
根据这个图可以看出,第五步会在Servlet类用到转向操作,转向操作的方法如下例:
- // 转向到新的servlet
- request.getRequestDispatcher( "main.html" ).forward(request, response);
如何测试具有转向功能的Servlet类呢?首先要明确对于这一类Servlet,我们要测试它们的什么功能:
第一, Servlet类在转向前都保存了哪些数据?保存这些数据的位置在哪儿?
第二, Servlet类是否转向到正确的位置上了?
需要注意的是,通常情况下作为Controller的Servlet类是要转向到作为View的JSP网页的,但是HttpUnit程序包不提供解析JSP网页的方法。为此,我们可以利用stub技术,利用另一个Servlet类为其模拟一个转向目标。
模拟转向目标的任务有两个:
第一, 从数据保存区提取相关的数据;
第二, 将相关的数据以响应的方式向用户端发送。
作为stub的Servlet类不需要进行数据的有效性判断。样例代码如下:
- import java.io.IOException;
- import java.io.PrintWriter;
- import javax.servlet.ServletException;
- import javax.servlet.http.HttpServlet;
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
- public class MainStub extends HttpServlet {
- @Override
- protected void doGet(HttpServletRequest req, HttpServletResponse resp)
- throws ServletException, IOException {
- // 从数据保存区提取相关的数据
- String userId = (String)req.getAttribute( "userId" );
- resp.setContentType( "text/plain" );
- resp.setCharacterEncoding( "UTF-8" );
- // 将相关的数据以响应的方式向用户端发送
- PrintWriter pw = resp.getWriter();
- pw.write(userId);
- }
- }
相应的,用户端测试代码的任务是:
第一, 注册需要测试的Servlet类与用作stub的Servlet类;
第二, 模拟调用需要测试的Servlet类并为其提供参数;
第三, 检查从用作stub的Servlet类中返回的响应数据是否符合要求。
样例代码如下:
- import junit.framework.TestCase;
- import com.meterware.httpunit.GetMethodWebRequest;
- import com.meterware.httpunit.WebRequest;
- import com.meterware.httpunit.WebResponse;
- import com.meterware.servletunit.ServletRunner;
- import com.meterware.servletunit.ServletUnitClient;
- public class HelloTest extends TestCase {
- public void testHelloServlet() throws Exception {
- ServletRunner sr = new ServletRunner();
- // 注册测试用Servlet
- sr.registerServlet( "hello.html" , HelloServlet. class .getName());
- // 注册stub用Servlet
- sr.registerServlet( "main.html" , MainStub. class .getName());
- ServletUnitClient sc = sr.newClient();
- // 调用测试用Servlet并为其提供参数
- WebRequest request =
- new GetMethodWebRequest( "http://localhost/hello.html?userId=darxin" );
- WebResponse response = sc.getResponse( request );
- // 检查最终的返回结果
- assertEquals( "darxin" , response.getText());
- }
- }
根据测试代码及stub代码,我们最终需要完成的Servlet类代码如下:
- import java.io.IOException;
- import javax.servlet.ServletException;
- import javax.servlet.http.HttpServlet;
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
- public class HelloServlet extends HttpServlet {
- @Override
- protected void doGet(HttpServletRequest req, HttpServletResponse resp)
- throws ServletException, IOException {
- // 从请求中取出参数
- String userId = req.getParameter( "userId" );
- // 向request中设置属性
- req.setAttribute( "userId" , userId);
- // 转向到新的servlet
- req.getRequestDispatcher( "main.html" ).forward(req, resp);
- }
- }
以上简要说明了如何利用HttpUnit程序包测试Servlet的方法,此方法适用于基本的Servlet实现。
对于容器内测试,建议使用Cactus技术。