我在测试这个时遇到问题。
线路 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/