java - 模拟 servlet,我该如何正确地模拟它?

标签 java junit mockito

我在测试这个时遇到问题。

线路 ServletContext servletContext = getServletContext(); 给我带来悲伤,这应该被 mock 吗?如果是的话我会怎么做? 我得到的错误是来自它。 java.lang.IllegalStateException:ServletConfig尚未初始化

我的代码是

  public class StockSearchServlet extends HttpServlet {
  public void doPost(HttpServletRequest request, HttpServletResponse response)
        throws IOException, ServletException {
    String dateOut = "yyyy/MM/dd";        
    DateTimeFormatter ldtf = DateTimeFormat.forPattern(dateOut);
    String symbol = request.getParameter("symbol");
    String from = request.getParameter("from");
    DateTime fromDate = new DateTime(ldtf.parseDateTime(from));
    String until = request.getParameter("until");
    DateTime untilDate = new DateTime(ldtf.parseDateTime(until));
    String interval = request.getParameter("interval");
    HttpSession session = request.getSession();
    StockQuery stockQuery = null;
    try {
        stockQuery = new StockQuery(symbol,from,until);
        stockQuery.setInterval(interval);
    } catch (ParseException ex) {
        Logger.getLogger(StockSearchServlet.class.getName()).log(Level.SEVERE, null, ex);
    }
    session.setAttribute("stockQuery", stockQuery);        
    StockService stockService = ServiceFactory.getStockServiceInstance();
    List<StockQuote> quotes = new ArrayList<StockQuote>();

    StringBuilder stringBuilder = new StringBuilder();

    try {
        quotes = stockService.getQuote(symbol,fromDate, untilDate ,StockService.getIntervalEnum(interval));
    } catch (StockServiceException ex) {
        Logger.getLogger(StockSearchServlet.class.getName()).log(Level.SEVERE, null, ex);
    }
    for (StockQuote stockQuote : quotes) {
        stringBuilder.append("<br>"+ stockQuote.getSymbol() + " " + stockQuote.getPrice() + " " + stockQuote.getDate());
    }
    String outputStockString = stringBuilder.toString();
    session.setAttribute("outputStockString", outputStockString);

    ServletContext servletContext = getServletContext();
    RequestDispatcher dispatcher = servletContext.getRequestDispatcher("/stockquoteResults.jsp");
    dispatcher.forward(request, response);
}

这是我当前使用的测试

  @Test
  public void testDoPost() throws IOException, ServletException {
    String symbol = "GOOG";
    String from = "2015/10/05";
    String until = "2015/10/09";
    String interval = "day";

    StockSearchServlet stockSearchServlet = new StockSearchServlet();
    HttpServletRequest request = mock(HttpServletRequest.class);
    when(request.getParameter("symbol")).thenReturn(symbol);
    when(request.getParameter("from")).thenReturn(from);
    when(request.getParameter("until")).thenReturn(until);
    when(request.getParameter("interval")).thenReturn(interval);
    when(request.getSession()).thenReturn(new MySession());
    HttpServletResponse response = mock(HttpServletResponse.class);
    //when(request.getSession()).thenReturn(new MySession());
    new StockSearchServlet().doPost(request, response);
    //stockSearchServlet.doPost(request, response);

    String outputString = (String) request.getSession().getAttribute("outputStockString");
    assertEquals("verify state", outputString.length(), 1);
}

这个类是测试类的一部分。

   class MySession implements HttpSession {
    private Map<String, Object> attributes = new HashMap<String, Object>();

    @Override
    public long getCreationTime() {
        return 0;
    }

    @Override
    public String getId() {
        return null;
    }

    @Override
    public long getLastAccessedTime() {
        return 0;
    }

    @Override
    public ServletContext getServletContext() {
        return new ServletContext() {
            @Override
            public String getContextPath() {
                return null;
            }
            @Override
            public ServletContext getContext(String s) {
                return null;
            }
            @Override
            public int getMajorVersion() {
                return 0;
            }
            @Override
            public int getMinorVersion() {
                return 0;
            }
            @Override
            public int getEffectiveMajorVersion() {
                return 0;
            }
            @Override
            public int getEffectiveMinorVersion() {
                return 0;
            }
            @Override
            public String getMimeType(String s) {
                return null;
            }
            @Override
            public Set<String> getResourcePaths(String s) {
                return null;
            }
            @Override
            public URL getResource(String s) throws MalformedURLException {
                return null;
            }
            @Override
            public InputStream getResourceAsStream(String s) {
                return null;
            }
            @Override
            public RequestDispatcher getRequestDispatcher(String s) {
                return new RequestDispatcher() {
                    @Override
                    public void forward(ServletRequest servletRequest, ServletResponse servletResponse)
                            throws ServletException, IOException {
                    }
                    @Override            
                    public void include(ServletRequest servletRequest, ServletResponse servletResponse)
                            throws ServletException, IOException {
                    }
                };
            }
            @Override
            public RequestDispatcher getNamedDispatcher(String s) {
                return null;
            }
            @Override
            public Servlet getServlet(String s) throws ServletException {
                return null;
            }
            @Override
            public Enumeration<Servlet> getServlets() {
                return null;
            }
            @Override
            public Enumeration<String> getServletNames() {
                return null;
            }
            @Override
            public void log(String s) {
            }
            @Override
            public void log(Exception e, String s) {
            }
            @Override
            public void log(String s, Throwable throwable) {
            }
            @Override
            public String getRealPath(String s) {
                return null;
            }
            @Override
            public String getServerInfo() {
                return null;
            }
            @Override
            public String getInitParameter(String s) {
                return null;
            }
            @Override
            public Enumeration<String> getInitParameterNames() {
                return null;
            }
            @Override
            public boolean setInitParameter(String s, String s1) {
                return false;
            }
            @Override
            public Object getAttribute(String s) {
                return null;
            }
            @Override
            public Enumeration<String> getAttributeNames() {
                return null;
            }
            @Override
            public void setAttribute(String s, Object o) {
            }
            @Override
            public void removeAttribute(String s) {
            }
            @Override
            public String getServletContextName() {
                return null;
            }
            @Override
            public ServletRegistration.Dynamic addServlet(String s, String s1) {
                return null;
            }
            @Override
            public ServletRegistration.Dynamic addServlet(String s, Servlet servlet) {
                return null;
            }
            @Override
            public ServletRegistration.Dynamic addServlet(String s, Class<? extends Servlet> aClass) {
                return null;
            }
            @Override
            public <T extends Servlet> T createServlet(Class<T> aClass) throws ServletException {
                return null;
            }
            @Override
            public ServletRegistration getServletRegistration(String s) {
                return null;
            }
            @Override
            public Map<String, ? extends ServletRegistration> getServletRegistrations() {
                return null;
            }
            @Override
            public FilterRegistration.Dynamic addFilter(String s, String s1) {
                return null;
            }
            @Override
            public FilterRegistration.Dynamic addFilter(String s, Filter filter) {
                return null;
            }
            @Override
            public FilterRegistration.Dynamic addFilter(String s, Class<? extends Filter> aClass) {
               return null;
            }
            @Override
            public <T extends Filter> T createFilter(Class<T> aClass) throws ServletException {
                return null;
            }
            @Override
            public FilterRegistration getFilterRegistration(String s) {
                return null;
            }
            @Override
            public Map<String, ? extends FilterRegistration> getFilterRegistrations() {
                return null;
            }
            @Override
            public SessionCookieConfig getSessionCookieConfig() {
                return null;
            }
            @Override
            public void setSessionTrackingModes(Set<SessionTrackingMode> set) {
            }
            @Override
            public Set<SessionTrackingMode> getDefaultSessionTrackingModes() {
                return null;
            }
            @Override
            public Set<SessionTrackingMode> getEffectiveSessionTrackingModes() {
                return null;
            }
            @Override
            public void addListener(String s) {
            }
            @Override
            public <T extends EventListener> void addListener(T t) {
            }
            @Override
            public void addListener(Class<? extends EventListener> aClass) {
            }
            @Override
            public <T extends EventListener> T createListener(Class<T> aClass) throws ServletException {
                return null;
            }
            @Override
            public JspConfigDescriptor getJspConfigDescriptor() {
                return null;
            }
            @Override
            public ClassLoader getClassLoader() {
                return null;
            }
            @Override
            public void declareRoles(String... strings) {
            }
        };
    }

    @Override
    public void setMaxInactiveInterval(int interval) {
    }

    @Override
    public int getMaxInactiveInterval() {
        return 0;
    }

    @Override
    public HttpSessionContext getSessionContext() {
        return null;
    }

    @Override
    public Object getAttribute(String name) {
        return attributes.get(name);
    }

    @Override
    public Object getValue(String name) {
        return null;
    }

    @Override
    public Enumeration<String> getAttributeNames() {
        return null;
    }

    @Override
    public String[] getValueNames() {
        return new String[0];
    }

    @Override
    public void setAttribute(String name, Object value) {
        attributes.put(name,value);
    }

    @Override
    public void putValue(String name, Object value) {

    }

    @Override
    public void removeAttribute(String name) {

    }

    @Override
    public void removeValue(String name) {

    }

    @Override
    public void invalidate() {

    }

    @Override
    public boolean isNew() {
        return false;
    }
}

最佳答案

有一些方法可以通过一些小的重构来避免模拟 getServletContext()RequestDispatcher,或者您可以选择模拟它们。

这里有 3 种可能的方法:

1) 您可以将获取 ServletContext 并转发到 jsp 页面的代码( doPost() 的最后 3 行)放在 protected 方法中(我们称之为 dispatchToJSP() ),然后在您的测试您在 doPost() 的子类上调用 StockSearchServlet,它会覆盖 dispatchToJSP() 以执行断言来验证 jsp 路径。

例如,在您的测试中:

StockSearchServlet stockSearchServlet = new StockSearchServlet() {
  @Override
  protected void dispatchToJSP(String path) {
      assertEquals("verify jsp path", path, "/stockquoteResults.jsp");
  }
};

您可能还希望重写的方法在子类中设置一个 boolean 标志,指示该方法已被调用,在这种情况下,最好使用带有检索该标志的方法的非匿名子类。

2) 一种更优雅的方法可能是重构,将调度到 jsp 的任务委托(delegate)给专门的类(最好通过接口(interface) - 让我们称之为 IDispatcher ),该类只执行调度,然后您模拟该接口(interface)以验证是否使用正确的 jsp 路径调用它,然后创建一个单独的测试类以在隔离测试中正确测试真正的 IDispatcher 实现。因此,您可以将 doPost() 的最后 3 行替换为 this.dispatcher.forwardTo("/stockquoteResults.jsp");,并且在测试中,您可以在测试的 servlet 中设置一个模拟调度程序并调用 verify(mockDispatcher).forwardTo("/stockquoteResults.jsp"); 或类似的内容。这可能是产生最干净、设计最好、最可重用的代码的方法,但它需要更多的工作。如果您只有 1 个 servlet,则可能不值得进行额外的工作。

3) 如果您决定只模拟所有内容而不修改 StockSearchServlet 类本身,则测试 StockSearchServlet 子类的技巧允许您覆盖 getServletContext() 以返回模拟(这将反过来返回RequestDispatcher 的模拟):

final ServletContext myMockServletContext = mock(ServletContext.class);
StockSearchServlet stockSearchServlet = new StockSearchServlet() {
  @Override
  public ServletContext getServletContext() {
      return myMockServletContext;
  }
};

第三种方法很简单,但感觉很丑,因为它非常依赖于实现,并且有模拟返回模拟。不过,servlet API 不会经常更改,所以我认为这种情况是可以的,并且由于 servlet API 中的设计有问题,模拟必须返回模拟(为什么 forward() 中不只有一个 HttpServlet 方法?)。

关于java - 模拟 servlet,我该如何正确地模拟它?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33715509/

相关文章:

java - Joda 或 Java 日期和时间 API 中是否有任何本地化常量,例如 "Today"、 "Tomorrow"、 "Yesterday"等?

java - 在二叉树中搜索字符串

java - 升级到 SDK 1.9.17 后 JUnit 测试失败

java - PowerMockito VerifyStatic 在 2.0.0-beta5 中不起作用

java - 模拟验证

java - org.springframework.web.WebApplicationInitializer 未找到

java - Android float View (在其他 View 之上)

java - 为什么使用 JUnit 测试套件?

java - 当测试不是Java方法时如何让Eclipse跳转到失败的JUnit测试

android - 使用适用于 Android 的 Mockito 进行测试