如何使用JUnittesting我的servlet

我已经使用Java Servlets创build了一个Web系统,现在想要进行JUnittesting。 我的dataManager只是将其提交给数据库的基本代码片段。 你将如何用JUnittesting一个Servlet?

我的代码示例,允许用户注册/注册,这是从我的主页通过AJAX提交:

 public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{ // Get parameters String userName = request.getParameter("username"); String password = request.getParameter("password"); String name = request.getParameter("name"); try { // Load the database driver Class.forName("com.mysql.jdbc.Driver"); //pass reg details to datamanager dataManager = new DataManager(); //store result as string String result = dataManager.register(userName, password, name); //set response to html + no cache response.setContentType("text/html"); response.setHeader("Cache-Control", "no-cache"); //send response with register result response.getWriter().write(result); } catch(Exception e){ System.out.println("Exception is :" + e); } } 

你可以用Mockito来做这件事 ,让模拟返回正确的参数,确认它们确实被调用(可选地指定次数),写出“结果”并确认它是正确的。

 import static org.junit.Assert.*; import static org.mockito.Mockito.*; import java.io.*; import javax.servlet.http.*; import org.apache.commons.io.FileUtils; import org.junit.Test; public class TestMyServlet extends Mockito{ @Test public void testServlet() throws Exception { HttpServletRequest request = mock(HttpServletRequest.class); HttpServletResponse response = mock(HttpServletResponse.class); when(request.getParameter("username")).thenReturn("me"); when(request.getParameter("password")).thenReturn("secret"); StringWriter stringWriter = new StringWriter(); PrintWriter writer = new PrintWriter(stringWriter); when(response.getWriter()).thenReturn(writer); new MyServlet().doPost(request, response); verify(request, atLeast(1)).getParameter("username"); // only if you want to verify username was called... writer.flush(); // it may not have been flushed yet... assertTrue(writer.toString().contains("My expected string")); } 

首先,在真正的应用程序中,您永远不会在servlet中获得数据库连接信息; 你可以在你的应用服务器上configuration它。

但是,有些方法可以在不运行容器的情况下testingServlet。 一个是使用模拟对象。 Spring为HttpServletRequest,HttpServletResponse,HttpServletSession等提供了一组非常有用的模拟:

http://static.springsource.org/spring/docs/3.0.x/api/org/springframework/mock/web/package-summary.html

使用这些模拟,你可以testing的东西

如果用户名不在请求中,会发生什么?

如果用户名在请求中会发生什么?

等等

然后你可以做这样的事情:

 import static org.junit.Assert.assertEquals; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.junit.Before; import org.junit.Test; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpServletResponse; public class MyServletTest { private MyServlet servlet; private MockHttpServletRequest request; private MockHttpServletResponse response; @Before public void setUp() { servlet = new MyServlet(); request = new MockHttpServletRequest(); response = new MockHttpServletResponse(); } @Test public void correctUsernameInRequest() throws ServletException, IOException { request.addParameter("username", "scott"); request.addParameter("password", "tiger"); servlet.doPost(request, response); assertEquals("text/html", response.getContentType()); // ... etc } } 

这里有另外一个select,使用OpenBrace的ObMimic Servlet API test-double库(披露:我是它的开发者)。

 package com.openbrace.experiments.examplecode.stackoverflow5434419; import static org.junit.Assert.*; import com.openbrace.experiments.examplecode.stackoverflow5434419.YourServlet; import com.openbrace.obmimic.mimic.servlet.ServletConfigMimic; import com.openbrace.obmimic.mimic.servlet.http.HttpServletRequestMimic; import com.openbrace.obmimic.mimic.servlet.http.HttpServletResponseMimic; import com.openbrace.obmimic.substate.servlet.RequestParameters; import org.junit.Before; import org.junit.Test; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; /** * Example tests for {@link YourServlet#doPost(HttpServletRequest, * HttpServletResponse)}. * * @author Mike Kaufman, OpenBrace Limited */ public class YourServletTest { /** The servlet to be tested by this instance's test. */ private YourServlet servlet; /** The "mimic" request to be used in this instance's test. */ private HttpServletRequestMimic request; /** The "mimic" response to be used in this instance's test. */ private HttpServletResponseMimic response; /** * Create an initialized servlet and a request and response for this * instance's test. * * @throws ServletException if the servlet's init method throws such an * exception. */ @Before public void setUp() throws ServletException { /* * Note that for the simple servlet and tests involved: * - We don't need anything particular in the servlet's ServletConfig. * - The ServletContext isn't relevant, so ObMimic can be left to use * its default ServletContext for everything. */ servlet = new YourServlet(); servlet.init(new ServletConfigMimic()); request = new HttpServletRequestMimic(); response = new HttpServletResponseMimic(); } /** * Test the doPost method with example argument values. * * @throws ServletException if the servlet throws such an exception. * @throws IOException if the servlet throws such an exception. */ @Test public void testYourServletDoPostWithExampleArguments() throws ServletException, IOException { // Configure the request. In this case, all we need are the three // request parameters. RequestParameters parameters = request.getMimicState().getRequestParameters(); parameters.set("username", "mike"); parameters.set("password", "xyz#zyx"); parameters.set("name", "Mike"); // Run the "doPost". servlet.doPost(request, response); // Check the response's Content-Type, Cache-Control header and // body content. assertEquals("text/html; charset=ISO-8859-1", response.getMimicState().getContentType()); assertArrayEquals(new String[] { "no-cache" }, response.getMimicState().getHeaders().getValues("Cache-Control")); assertEquals("...expected result from dataManager.register...", response.getMimicState().getBodyContentAsString()); } } 

笔记:

  • 每个“模仿”对于其逻辑状态都有一个“mimicState”对象。 这提供了Servlet API方法和模拟器内部状态的configuration和检查之间的明显区别。

  • 您可能会惊讶于Content-Type的检查包含“charset = ISO-8859-1”。 但是,对于给定的“doPost”代码,这是根据Servlet API Javadoc和HttpServletResponse自己的getContentType方法,以及在例如Glassfish 3上生成的实际的Content-Type头。如果使用正常的模拟对象,而您自己对API行为的期望。 在这种情况下,这可能没有关系,但在更复杂的情况下,这是一种无法预料的API行为,可能会对嘲笑有点嘲笑!

  • 我已经使用response.getMimicState().getContentType()作为检查Content-Type的最简单的方法,并说明了上述观点,但是如果你愿意的话,你确实可以检查“text / html”(使用response.getMimicState().getContentTypeMimeType() )。 检查Content-Type标头的方式与Cache-Control标头相同。

  • 在这个例子中,响应内容被检查为字符数据(使用Writer的编码)。 我们还可以检查响应的Writer是否被使用,而不是它的OutputStream(使用response.getMimicState().isWritingCharacterContent() ),但是我认为我们只关心结果输出,不关心什么API调用产生了它(虽然可以检查…)。 也可以以字节的forms检索响应的主体内容,检查Writer / OutputStream的详细状态等。

有ObMimic的全部细节,并在OpenBrace网站免费下载。 或者,如果您有任何问题,可以联系我(联系方式在网站上)。

我发现Seleniumtesting对集成或function(端到端)testing更有用。 我正在尝试使用org.springframework.mock.web ,但我不是很远。 我正在附加一个带有jMocktesting套件的示例控制器。

首先,主持人:

 package com.company.admin.web; import javax.validation.Valid; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.validation.BindingResult; import org.springframework.validation.ObjectError; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.SessionAttributes; import org.springframework.web.bind.support.SessionStatus; import com.company.admin.domain.PaymentDetail; import com.company.admin.service.PaymentSearchService; import com.company.admin.service.UserRequestAuditTrail; import com.company.admin.web.form.SearchCriteria; /** * Controls the interactions regarding to the refunds. * * @author slgelma * */ @Controller @SessionAttributes({"user", "authorization"}) public class SearchTransactionController { public static final String SEARCH_TRANSACTION_PAGE = "searchtransaction"; private PaymentSearchService searchService; //private Validator searchCriteriaValidator; private UserRequestAuditTrail notifications; @Autowired public void setSearchService(PaymentSearchService searchService) { this.searchService = searchService; } @Autowired public void setNotifications(UserRequestAuditTrail notifications) { this.notifications = notifications; } @RequestMapping(value="/" + SEARCH_TRANSACTION_PAGE) public String setUpTransactionSearch(Model model) { SearchCriteria searchCriteria = new SearchCriteria(); model.addAttribute("searchCriteria", searchCriteria); notifications.transferTo(SEARCH_TRANSACTION_PAGE); return SEARCH_TRANSACTION_PAGE; } @RequestMapping(value="/" + SEARCH_TRANSACTION_PAGE, method=RequestMethod.POST, params="cancel") public String cancelSearch() { notifications.redirectTo(HomeController.HOME_PAGE); return "redirect:/" + HomeController.HOME_PAGE; } @RequestMapping(value="/" + SEARCH_TRANSACTION_PAGE, method=RequestMethod.POST, params="execute") public String executeSearch( @ModelAttribute("searchCriteria") @Valid SearchCriteria searchCriteria, BindingResult result, Model model, SessionStatus status) { //searchCriteriaValidator.validate(criteria, result); if (result.hasErrors()) { notifications.transferTo(SEARCH_TRANSACTION_PAGE); return SEARCH_TRANSACTION_PAGE; } else { PaymentDetail payment = searchService.getAuthorizationFor(searchCriteria.geteWiseTransactionId()); if (payment == null) { ObjectError error = new ObjectError( "eWiseTransactionId", "Transaction not found"); result.addError(error); model.addAttribute("searchCriteria", searchCriteria); notifications.transferTo(SEARCH_TRANSACTION_PAGE); return SEARCH_TRANSACTION_PAGE; } else { model.addAttribute("authorization", payment); notifications.redirectTo(PaymentDetailController.PAYMENT_DETAIL_PAGE); return "redirect:/" + PaymentDetailController.PAYMENT_DETAIL_PAGE; } } } } 

接下来,testing:

  package test.unit.com.company.admin.web; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; import static org.junit.Assert.assertThat; import org.jmock.Expectations; import org.jmock.Mockery; import org.jmock.integration.junit4.JMock; import org.jmock.integration.junit4.JUnit4Mockery; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.ui.Model; import org.springframework.validation.BindingResult; import org.springframework.validation.ObjectError; import org.springframework.web.bind.support.SessionStatus; import com.company.admin.domain.PaymentDetail; import com.company.admin.service.PaymentSearchService; import com.company.admin.service.UserRequestAuditTrail; import com.company.admin.web.HomeController; import com.company.admin.web.PaymentDetailController; import com.company.admin.web.SearchTransactionController; import com.company.admin.web.form.SearchCriteria; /** * Tests the behavior of the SearchTransactionController. * @author slgelma * */ @RunWith(JMock.class) public class SearchTransactionControllerTest { private final Mockery context = new JUnit4Mockery(); private final SearchTransactionController controller = new SearchTransactionController(); private final PaymentSearchService searchService = context.mock(PaymentSearchService.class); private final UserRequestAuditTrail notifications = context.mock(UserRequestAuditTrail.class); private final Model model = context.mock(Model.class); /** * @throws java.lang.Exception */ @Before public void setUp() throws Exception { controller.setSearchService(searchService); controller.setNotifications(notifications); } @Test public void setUpTheSearchForm() { final String target = SearchTransactionController.SEARCH_TRANSACTION_PAGE; context.checking(new Expectations() {{ oneOf(model).addAttribute( with(any(String.class)), with(any(Object.class))); oneOf(notifications).transferTo(with(any(String.class))); }}); String nextPage = controller.setUpTransactionSearch(model); assertThat("Controller is not requesting the correct form", target, equalTo(nextPage)); } @Test public void cancelSearchTest() { final String target = HomeController.HOME_PAGE; context.checking(new Expectations(){{ never(model).addAttribute(with(any(String.class)), with(any(Object.class))); oneOf(notifications).redirectTo(with(any(String.class))); }}); String nextPage = controller.cancelSearch(); assertThat("Controller is not requesting the correct form", nextPage, containsString(target)); } @Test public void executeSearchWithNullTransaction() { final String target = SearchTransactionController.SEARCH_TRANSACTION_PAGE; final SearchCriteria searchCriteria = new SearchCriteria(); searchCriteria.seteWiseTransactionId(null); final BindingResult result = context.mock(BindingResult.class); final SessionStatus status = context.mock(SessionStatus.class); context.checking(new Expectations() {{ allowing(result).hasErrors(); will(returnValue(true)); never(model).addAttribute(with(any(String.class)), with(any(Object.class))); never(searchService).getAuthorizationFor(searchCriteria.geteWiseTransactionId()); oneOf(notifications).transferTo(with(any(String.class))); }}); String nextPage = controller.executeSearch(searchCriteria, result, model, status); assertThat("Controller is not requesting the correct form", target, equalTo(nextPage)); } @Test public void executeSearchWithEmptyTransaction() { final String target = SearchTransactionController.SEARCH_TRANSACTION_PAGE; final SearchCriteria searchCriteria = new SearchCriteria(); searchCriteria.seteWiseTransactionId(""); final BindingResult result = context.mock(BindingResult.class); final SessionStatus status = context.mock(SessionStatus.class); context.checking(new Expectations() {{ allowing(result).hasErrors(); will(returnValue(true)); never(model).addAttribute(with(any(String.class)), with(any(Object.class))); never(searchService).getAuthorizationFor(searchCriteria.geteWiseTransactionId()); oneOf(notifications).transferTo(with(any(String.class))); }}); String nextPage = controller.executeSearch(searchCriteria, result, model, status); assertThat("Controller is not requesting the correct form", target, equalTo(nextPage)); } @Test public void executeSearchWithTransactionNotFound() { final String target = SearchTransactionController.SEARCH_TRANSACTION_PAGE; final String badTransactionId = "badboy"; final PaymentDetail transactionNotFound = null; final SearchCriteria searchCriteria = new SearchCriteria(); searchCriteria.seteWiseTransactionId(badTransactionId); final BindingResult result = context.mock(BindingResult.class); final SessionStatus status = context.mock(SessionStatus.class); context.checking(new Expectations() {{ allowing(result).hasErrors(); will(returnValue(false)); atLeast(1).of(model).addAttribute(with(any(String.class)), with(any(Object.class))); oneOf(searchService).getAuthorizationFor(with(any(String.class))); will(returnValue(transactionNotFound)); oneOf(result).addError(with(any(ObjectError.class))); oneOf(notifications).transferTo(with(any(String.class))); }}); String nextPage = controller.executeSearch(searchCriteria, result, model, status); assertThat("Controller is not requesting the correct form", target, equalTo(nextPage)); } @Test public void executeSearchWithTransactionFound() { final String target = PaymentDetailController.PAYMENT_DETAIL_PAGE; final String goodTransactionId = "100000010"; final PaymentDetail transactionFound = context.mock(PaymentDetail.class); final SearchCriteria searchCriteria = new SearchCriteria(); searchCriteria.seteWiseTransactionId(goodTransactionId); final BindingResult result = context.mock(BindingResult.class); final SessionStatus status = context.mock(SessionStatus.class); context.checking(new Expectations() {{ allowing(result).hasErrors(); will(returnValue(false)); atLeast(1).of(model).addAttribute(with(any(String.class)), with(any(Object.class))); oneOf(searchService).getAuthorizationFor(with(any(String.class))); will(returnValue(transactionFound)); oneOf(notifications).redirectTo(with(any(String.class))); }}); String nextPage = controller.executeSearch(searchCriteria, result, model, status); assertThat("Controller is not requesting the correct form", nextPage, containsString(target)); } } 

我希望这可能有帮助。

  public class WishServletTest { WishServlet wishServlet; HttpServletRequest mockhttpServletRequest; HttpServletResponse mockhttpServletResponse; @Before public void setUp(){ wishServlet=new WishServlet(); mockhttpServletRequest=createNiceMock(HttpServletRequest.class); mockhttpServletResponse=createNiceMock(HttpServletResponse.class); } @Test public void testService()throws Exception{ File file= new File("Sample.txt"); File.createTempFile("ashok","txt"); expect(mockhttpServletRequest.getParameter("username")).andReturn("ashok"); expect(mockhttpServletResponse.getWriter()).andReturn(new PrintWriter(file)); replay(mockhttpServletRequest); replay(mockhttpServletResponse); wishServlet.doGet(mockhttpServletRequest, mockhttpServletResponse); FileReader fileReader=new FileReader(file); int count = 0; String str = ""; while ( (count=fileReader.read())!=-1){ str=str+(char)count; } Assert.assertTrue(str.trim().equals("Helloashok")); verify(mockhttpServletRequest); verify(mockhttpServletResponse); } } 

编辑 :仙人掌现在是一个死的项目: http : //attic.apache.org/projects/jakarta-cactus.html


你可能想看看仙人掌。

http://jakarta.apache.org/cactus/

项目介绍

仙人掌是一个简单的testing框架,用于unit testing服务器端的java代码(Servlets,EJBs,Tag Libs,Filters,…)。

仙人掌的目的是为了降低编写服务器端代码testing的成本。 它使用JUnit并对其进行扩展。

仙人掌实施了容器内的策略,这意味着testing在容器内执行。

另一种方法是创build一个embedded式服务器来“托pipe”你的servlet,允许你使用库调用实际的服务器来调用它(这种方法的有用性在一定程度上取决于你如何轻松地进行“合法的”编程调用服务器 – 我正在testing一个JMS(Java消息服务)接入点,客户端比比皆是)。

有几条不同的路线你可以去 – 通常两个是tomcat和docker。

警告:select要embedded的服务器时需要注意的一点是您正在使用的servlet-api版本(提供类似HttpServletRequest的库)。 如果你使用2.5,我发现Jetty 6.x工作得很好(这是我将在下面给出的例子)。 如果你使用的是servlet-api 3.0,那么tomcat-7embedded的东西似乎是一个不错的select,但是我不得不放弃使用它的尝试,因为我testing的应用程序使用了servlet-api 2.5。 在尝试configuration或启动服务器时,试图混合使用这两者将导致NoSuchMethod和其他此类exception。

你可以像这样设置一个这样的服务器(Jetty 6.1.26,servlet-api 2.5):

 public void startServer(int port, Servlet yourServletInstance){ Server server = new Server(port); Context root = new Context(server, "/", Context.SESSIONS); root.addServlet(new ServletHolder(yourServletInstance), "/servlet/context/path"); //If you need the servlet context for anything, such as spring wiring, you coudl get it like this //ServletContext servletContext = root.getServletContext(); server.start(); } 

首先你可能应该重构这一点,所以DataManager不是在doPost代码中创build的..你应该尝试dependency injection来获得一个实例。 (请参阅Guicevideo,了解DI的一个很好的介绍)。 如果你被告知开始unit testing,那么DI是必须的。

一旦你的dependency injection,你可以孤立地testing你的类。

为了真正testing这个servlet,还有其他一些讨论过这个问题的线程。试试这里和这里 。

使用Selenium进行基于web的unit testing。 有一个名为Selenium IDE的Firefox插件,它可以logging网页上的动作并导出到使用Selenium RC运行testing服务器的JUnittesting用例。

只要incase上面的答案不再使用新版本的Mockito而不是使用mock()when() Mockito.mock()Mockito.when()应该使用