相信大家有好多人会使用JUNIT进行单元测试了,但是在WEB开发中仅仅对MVC设计模式中业务逻辑进行测试还是远远不够的,如何对 控制器和视图层进行单元测试将是本文的重点:
主要分享的内容:
一.测试驱动 Java Servlet
二.测试驱动 Spring 控制器
三.用JspTest 测试驱动JSP
开始吧。
一.测试驱动 Java Servlet
servlet通常会有一两个公有方法,每个公有方法处理一种类型的HTTP请求,例如:GET,POST,HEAD,PUT等。每个方法都以doXXX格式命名。大部分情况下都只处理GET和POST请求而已。基类javax.servlet.http.HttpServlet的service方法负责处理所有HTTP请求,其会根据HTTP请求类型把请求转发到对应的doXXX方法上。
doXXX方法的第一个参数HttpServletRequest封装了HTTP请求,其包含请求相关参数,HTTP头及其他信息。
第二个参数HttpServletResponse主要用于给客户端回送应答。任何发给浏览器的HTTP头都可以放到应答对象中。所有想输出的内容都可以转给应答中的printWriter对象和ServletOutputStream对象。
实例:
1.模拟请求和应答
package com.tddinaction.j2ee.web.controller.servlet;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Enumeration;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class EchoServlet extends HttpServlet {
protected void doGet(HttpServletRequest request,
HttpServletResponse response) throws ServletException,
IOException {
PrintWriter writer = response.getWriter();
for (Enumeration e = request.getParameterNames(); e.hasMoreElements(); ) {
String parameter = String.valueOf(e.nextElement());
String[] values = request.getParameterValues(parameter);
for (int i = 0; i < values.length; i++) {
writer.write(parameter + "=" + values[i]);
writer.write("\n");
}
}
writer.close();
}
}
package com.tddinaction.j2ee.web.controller.servlet;
import junit.framework.TestCase;
import org.junit.Test;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
public class EchoServletTest extends TestCase {
@Test
public void testEchoingParametersWithMultipleValues() throws Exception {
//通过第三方的测试替身来模拟请求对象和应答对象。用来模拟浏览器发起的请求
MockHttpServletRequest request = new MockHttpServletRequest();
MockHttpServletResponse response = new MockHttpServletResponse();
request.addParameter("param1", "param1value1");
request.addParameter("param2", "param2value1");
request.addParameter("param2", "param2value2");
new EchoServlet().doGet(request, response);
String[] lines = response.getContentAsString().split("\n");
assertEquals("Expected as many lines as we have parameter values", 3, lines.length);
assertEquals("param1=param1value1", lines[0]);
assertEquals("param2=param2value1", lines[1]);
assertEquals("param2=param2value2", lines[2]);
}
}
2.测试重定向
package com.tddinaction.j2ee.web.controller.servlet;
import java.io.IOException;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class LoginServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String user = request.getParameter("j_username");
String pass = request.getParameter("j_password");
if (isValidLogin(user, pass)) {
request.getSession().setAttribute("username", user);
response.sendRedirect("/frontpage");
} else {
forwardTo("/invalidlogin", request, response);
}
}
private boolean isValidLogin(String user, String pass) {
if (pass.equals("correctpassword")) {
return true;
} else if (pass.equals("wrongpassword")) {
return false;
}
return false;
}
private void forwardTo(String path, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
RequestDispatcher dispatcher = request.getRequestDispatcher(path);
dispatcher.forward(request, response);
}
}
package com.tddinaction.j2ee.web.controller.servlet;
import static org.junit.Assert.assertEquals;
import org.junit.Before;
import org.junit.Test;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
public class TestLoginServlet {
private static final String VALID_USERNAME = "validuser";
private LoginServlet servlet;
private MockHttpServletRequest request;
private MockHttpServletResponse response;
@Before
public void setUp() {
servlet = new LoginServlet();
request = new MockHttpServletRequest();
response = new MockHttpServletResponse();
}
@Test
public void wrongPasswordShouldRedirectToErrorPage() throws Exception {
request.addParameter("j_username", VALID_USERNAME);
request.addParameter("j_password", "wrongpassword");
servlet.service(request, response);
assertUserEndedUpOnPage("/invalidlogin");
}
@Test
public void validLoginForwardsToFrontPageAndStoresUsername() throws Exception {
request.addParameter("j_username", VALID_USERNAME);
request.addParameter("j_password", "correctpassword");
servlet.service(request, response);
assertUserEndedUpOnPage("/frontpage");
assertEquals(VALID_USERNAME, request.getSession().getAttribute("username"));
}
private void assertUserEndedUpOnPage(String expected) {
String actual = response.getRedirectedUrl();
if (actual == null) {
actual = response.getForwardedUrl();
}
assertEquals(expected, actual);
}
}
二.测试驱动 Spring 控制器
1.失败的登录:
package com.tddinaction.j2ee.web.controller.spring;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;
import com.tddinaction.j2ee.web.controller.authenticator.Authenticator;
public class LoginController implements Controller {
private Authenticator authenticator;
public void setAuthenticator(Authenticator authenticator) {
this.authenticator = authenticator;
}
public ModelAndView handleRequest(HttpServletRequest request,
HttpServletResponse response) throws Exception {
String user = request.getParameter("j_username");
String pass = request.getParameter("j_password");
if (authenticator.isValidLogin(user, pass)) {
return new ModelAndView("frontpage");
}
return new ModelAndView("wrongpassword");
}
}
package com.tddinaction.j2ee.web.controller.spring;
import static org.junit.Assert.assertEquals;
import org.junit.Before;
import org.junit.Test;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.web.servlet.ModelAndView;
import com.tddinaction.j2ee.web.controller.authenticator.MockAuthenticator;
public class TestLoginController {
private static final String CORRECT_PASSWORD = "correctpassword";
private static final String VALID_USERNAME = "validuser";
private MockHttpServletRequest request;
private MockHttpServletResponse response;
private LoginController controller;
@Before
public void setUp() {
request = new MockHttpServletRequest();
response = new MockHttpServletResponse();
controller = new LoginController();
MockAuthenticator auth = new MockAuthenticator();
auth.addUser(VALID_USERNAME, CORRECT_PASSWORD);
controller.setAuthenticator(auth);
}
@Test
public void wrongPasswordShouldRedirectToErrorPage() throws Exception {
request.addParameter("j_username", "noSuchUsername");
request.addParameter("j_password", "noSuchPassword");
ModelAndView v = controller.handleRequest(request, response);
assertEquals("wrongpassword", v.getViewName());
}
@Test
public void validLoginForwardsToFrontPageAndStoresUsername() throws Exception {
request.addParameter("j_username", VALID_USERNAME);
request.addParameter("j_password", CORRECT_PASSWORD);
ModelAndView v = controller.handleRequest(request, response);
assertEquals("frontpage", v.getViewName());
}
}
三.用JspTest 测试驱动JSP
JSPTEST是个开源库,他会把JSP编译成SERVLET类然后再执行此SERVLET。详细请登录其官网:
http://sourceforge.net/projects/jsptest/
jsptest里有一个叫做HtmlTestCase的抽象类,所有测试都需要继承这个类。HtmlTestCase提供了一些方法来模拟HTTP请求和渲染JSP文件以及做HTTP相关的验证等
JspTest默认在工程目录下开始寻找JSP文件。
测试FORM表单元素:
package com.tddinaction.j2ee.web.view.jsp.jsptest;
import net.sf.jsptest.HtmlTestCase;
public class LoginPageTest extends HtmlTestCase {
@Override
protected String getWebRoot() {
return "src/test/resources/websrc/jsp";
}
public void testFormFieldsArePresent() throws Exception {
get("/login.jsp");
form().shouldHaveField("j_username");
form().shouldHaveField("j_password");
form().shouldHaveField("ddd");
form().shouldHaveSubmitButton("login");
}
public void testPreviousUsernameIsRetained() throws Exception {
setRequestAttribute("j_username", "bob");
get("/login.jsp");
form().field("j_username").shouldHaveValue("bob");
}
}
//login.jsp
<%@ page language="Java" %>
<html>
<body>
<form>
<%
String username = (String) request.getAttribute("j_username");
if (username == null) username = "";
%>
<input type="text" name="j_username" value="<%= username %>"/>
<input type="password" name="j_password" />
<input type="submit" name="ddd" value="login" />
</form>
</body>
</html>