现在假设您要设计一个窗体登入Web程序,程序中有一个窗体登入页面,一个验证使用者名称与密码是否正确的LoginAction,以及一个登入成功页面,我们打算使用Spring的MVC框架来实现。
这个窗体登入程序最主要的当然就是验证使用者名称与密码是否正确的LoginAction,另两个只是页面呈现而已,我们希望对这个LoginAction进行单元测试,并希望采用「测试驱动」来进行程序开发,为此,我们要先设计好测试案例(TestCase)。
无疑的,我们的LoginAction必须从Request中取得窗体登入信息,然而这么作的话,我们的TestCase势必与Servlet容器相依赖,因而无法独立的进行纯綷的单元测试,而是「容器内测试」了,为了能独立于容器进行单元测试,我们设计一个LoginForm,它负责储存 Request中的窗体登入信息,而LoginAction上拥有一个handleForm()方法,专门负责处理LoginForm对象并返回一个 ModelAndView对象。
先不用考虑LoginForm如何从Request中收集登入信息,纯就上面的设计来考量,我们可以让我们的LoginAction与容器解耦合,以实现LoginAction的单元测试,在这边我们使用JUnit来进行测试,我们先写下以下的测试案例:
TestSpringappController.java
package onlyfun.caterpillar.test;
import junit.framework.TestCase;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;
import org.springframework.web.servlet.ModelAndView;
import onlyfun.caterpillar.LoginAction;
import onlyfun.caterpillar.LoginForm;
public class TestSpringappController extends TestCase {
private LoginAction loginAction;
private LoginForm form;
public static void main(String[] args) {
junit.textui.TestRunner.run(TestSpringappController.class);
}
protected void setUp() throws Exception {
super.setUp();
ApplicationContext applicationContext = new FileSystemXmlApplicationContext("/WEB-INF/hello-servlet.xml");
loginAction = (LoginAction) applicationContext.getBean("loginAction");
form = new LoginForm();
}
public void testLoginCorrect() {
form.setUsername("caterpillar");
form.setPassword("123456");
ModelAndView modelAndView = loginAction.handleForm(form);
assertEquals(loginAction.getSuccessView(), modelAndView.getViewName());
}
public void testLoginInCorrect() {
form.setUsername("somebody");
form.setPassword("123456");
ModelAndView modelAndView = loginAction.handleForm(form);
assertEquals(loginAction.getFormView(), modelAndView.getViewName());
form.setUsername("caterpillar");
form.setPassword("111111");
modelAndView = loginAction.handleForm(form);
assertEquals(loginAction.getFormView(), modelAndView.getViewName());
}
}
LoginAction中设定有getFormView()与getSuccessView(),分别表示登入失败与成功的页面,我们与 handleForm()传回的ModelAndView中的ViewName作断言,登入失败时使用getViewNAme()取得的名称,应该与 LoginAction的getFormView()取得的名称相同,而登入成功时使用getViewNAme()取得的名称,应该与 LoginAction的getSuccessView()取得的名称相同。注意到这个测试案例并没有使用到与Servlet API相关的对象,是个纯綷的单元测试。
依测试案例,我们可以设计我们的LoginForm与LoginAction,首先LoginForm如下,只是个纯綷的JavaBean:
LoginForm.java
package onlyfun.caterpillar;
public class LoginForm {
private String username;
private String password;
public void setUsername(String username) {
this.username = username;
}
public void setPassword(String password) {
this.password = password;
}
public String getUsername() {
return username;
}
public String getPassword() {
return password;
}
}
LoginAction.java
package onlyfun.caterpillar;
import java.io.IOException;
import javax.servlet.*;
import javax.servlet.http.*;
import org.springframework.web.servlet.mvc.Controller;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.bind.RequestUtils;
public class LoginAction implements Controller {
private String successView;
private String formView;
public ModelAndView handleRequest(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
String username = RequestUtils.getRequiredStringParameter(req, "username");
String password = RequestUtils.getRequiredStringParameter(req, "password");
LoginForm form = new LoginForm();
form.setUsername(username);
form.setUsername(password);
return handleForm(form);
}
public ModelAndView handleForm(Object formObject) {
LoginForm form = (LoginForm) formObject;
if("caterpillar".equals(form.getUsername()) &&
"123456".equals(form.getPassword())) {
return new ModelAndView(this.getSuccessView(),"user", form.getUsername());
}
else {
return new ModelAndView(this.getFormView());
}
}
public String getFormView() {
return formView;
}
public void setFormView(String formView) {
this.formView = formView;
}
public String getSuccessView() {
return successView;
}
public void setSuccessView(String successView) {
this.successView = successView;
}
}
在LoginAction中可以看到,透过LoginForm来收集Request中的参数,之后交给我们自订的handleForm(),我们可以实现与Servlet API的解耦合,使得单元测试变的可能。
当然,别忘了我们还要撰写Bean定义档:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING/DTD BEAN/EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<bean id="helloUserAction" class="onlyfun.caterpillar.HelloUserAction">
<property name="successView">
<value>/WEB-INF/jsp/success.jsp</value>
</property>
<property name="formView">
<value>/html/form.htm</value>
</property>
</bean>
</beans>
接下来您可以运行测试案例来进行测试了,在测试无误之后,我们就可以继续进行Spring MVC框架中其它的组件(component)配置了