4

我正在测试doPost()使用 EasyMock 对象作为HttpServletRequestHttpServletResponse参数的 servlet 方法。在doPost()我正在测试的方法中,请求和响应对象用作另一个类的静态方法类的参数,我想忽略(即不按预期记录)在此方法调用中对请求和响应对象进行的任何调用(无论如何它与这个测试无关)。例如,doPost()我正在测试的 servlet 类的方法如下所示:

@Override
protected void doPost(final HttpServletRequest servletRequest,
                      final HttpServletResponse servletResponse)
    throws ServletException, IOException
{
    // handle an "updateFolder" event
    String eventParameter = servletRequest.getParameter("event");
    if ("updateFolder".equalsIgnoreCase(eventParameter))
    {
        // update the news documents folder settings
        String folderId = servletRequest.getParameter("folderId");
        IPortletContext portletContext = PortletContextFactory.createPortletContext(servletRequest, servletResponse);
        IPortletResponse portletResponse = portletContext.getResponse();
        portletResponse.setSettingValue(SettingType.CommunityPortlet, "NEWS_DOCUMENTS_FOLDER_ID", folderId);
    }

    // redirect to the appropriate URL
    servletResponse.sendRedirect(redirectUrl);
}

当上面的代码到达PortletContextFactory.createPortletContext()被调用的步骤时,我并不关心对该方法中的请求和响应对象进行了哪些方法调用,但是如果我在测试此方法时传入模拟请求和响应对象,我会从中得到错误EasyMock 告诉我缺少行为定义。例如,我有一个如下所示的测试方法:

@Test
public void testPostWithUpdate()
    throws Exception
{
    // create mock objects and record their expected calls
    HttpServletRequest mockServletRequest = createMock(HttpServletRequest.class);
    HttpServletResponse mockServletResponse = createMock(HttpServletResponse.class);
    IPortletResponse mockPortletResponse = createMock(IPortletResponse.class);
    IPortletContext mockPortletContext = createMock(IPortletContext.class);
    expect(mockServletRequest.getContextPath()).andReturn(null);
    expect(mockServletRequest.getParameter("event")).andReturn("updateFolder");
    expect(mockServletRequest.getParameter("folderId")).andReturn(null);
    expect(PortletContextFactory.createPortletContext(mockServletRequest, mockServletResponse)).andReturn(mockPortletContext);
    expect(mockPortletContext.getResponse()).andReturn(mockPortletResponse);
    mockPortletResponse.setSettingValue(SettingType.CommunityPortlet, "NEWS_DOCUMENTS_FOLDER_ID", null);
    mockServletResponse.sendRedirect(EasyMock.anyObject(String.class));

    // take the mock objects out of record state
    replay(mockPortletContext, mockPortletResponse, mockServletRequest, mockServletResponse);

    // instantiate an object of the class and run the method we want to test
    ControllerServlet controllerServlet = new ControllerServlet();
    controllerServlet.doPost(mockServletRequest, mockServletResponse);

    // verify that our mocks behaved as expected
    verify(mockPortletContext, mockPortletResponse, mockServletRequest, mockServletResponse);
}

运行测试类时出现以下错误:

com.plumtree.openfoundation.util.XPIllegalStateException: missing behavior definition for the preceding method call getCharacterEncoding()
    at com.plumtree.openfoundation.util.XPException.GetInstance(XPException.java:397)
    at com.plumtree.openfoundation.util.XPException.GetInstance(XPException.java:350)
    at com.plumtree.openfoundation.web.XPRequest.InitRequest(XPRequest.java:201)
    at com.plumtree.openfoundation.web.XPRequest.<init>(XPRequest.java:111)
    at com.plumtree.remote.portlet.PortletContextFactory.createPortletContext(PortletContextFactory.java:32)
    at com.abc.servlet.ControllerServletTest.testPostWithUpdate(ControllerServletTest.java:31)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:44)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:41)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:76)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:193)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:52)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:191)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:42)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:184)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:236)
    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:49)
    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)
Caused by: java.lang.IllegalStateException: missing behavior definition for the preceding method call getCharacterEncoding()
    at org.easymock.internal.MockInvocationHandler.invoke(MockInvocationHandler.java:43)
    at org.easymock.internal.ObjectMethodsFilter.invoke(ObjectMethodsFilter.java:73)
    at $Proxy4.setCharacterEncoding(Unknown Source)
    at com.plumtree.openfoundation.web.XPRequest.InitRequest(XPRequest.java:135)
    ... 25 more

我假设上面的错误是由于没有记录PortletContextFactory.createPortletContext()在作为参数传入的模拟请求和响应对象的方法内执行的方法调用引起的。如果这实际上是这里发生的事情,那么我该如何返工,以便PortletContextFactory.createPortletContext()忽略该方法对请求和响应模拟的方法调用?

4

5 回答 5

2

试试 Mockito http://mockito.org/

从 EasyMock 使用起来要容易得多,并且不会强迫您对所有方法调用进行编码。

于 2010-09-17T21:19:35.500 回答
1

要关闭录音,请使用漂亮的模拟。从文档中:

对所有方法返回的 Mock 对象createMock()的默认行为是AssertionError为所有意外的方法调用抛出一个。如果您想要一个默认允许所有方法调用并返回适当的空值(0、null 或 false)的“漂亮”模拟对象,请createNiceMock()改用。

此外,更一般地说,您要模拟的是createPortletContext(servletRequest, servletResponse). 不幸的是,这是一个静态调用。要实现此模拟,请创建您自己的工厂,该工厂将返回portletContext,并将该工厂传递给您测试过的类(最好在构造函数中)。模拟这个工厂portletContext,所以你可以在这里只测试重要的东西:你写的代码。

于 2012-04-18T12:28:26.097 回答
1

在类似情况下进行测试的替代方法:

class Class_Under_Test {
  public void A() {
    B b = C.create(); //create is static
    D d = e.(b);
  }
}

要解决 EasyMock 中的静态引用问题,您可以将方法定义更改为:

@VisibleForTesting
B create() {
  return C.create();
}

public A() {
  B b = create();
  D d = e.(b); 
}

在您的测试课程中,您可以这样做:

Class testSomething {

  private Mock_Class_Under_Test mockOject;//Class Defined below 
  private B mockB;

  @Override
  void setup() {
    mockB  =createMock(B.class);
  }  

  private class Mock_Class_Under_Test extends Class_Under_Test {

      @Override
      B create() {
        return mockB;
      }
  }

  public void testA() {
    //No need to put expectation on static call as create() will always return mock.
    expect(e.something(mockB)).andReturn(somethingElse);   
  }
}

您所有进一步的测试都将使用 mockObject 来调用 Class_Under_Test 的方法。

于 2012-04-13T23:07:59.610 回答
1

你需要模拟PortletContextFactory.createPortletContext通话。EasyMock 本身不支持静态方法模拟,但是 EasyMock 的 PowerMock 扩展支持。这是您应该插入到测试中的示例代码:

mockStatic(PortletContextFactory.class);     
expect(PortletContextFactory.createPortletContext(mockServletRequest, mockServletResponse)).andReturn(mockPortletContext);
replay(PortletContextFactory.class);

还有2个要求:

  1. @RunWith(PowerMockRunner.class) 在测试用例的类级别使用 注释。
  2. @PrepareForTest(PortletContextFactory.class)在测试用例的类级别使用注释。

阅读更多:http ://code.google.com/p/powermock/wiki/MockStatic

于 2011-06-07T17:04:53.033 回答
1

也许你需要这样的东西:

expect(mockServletRequest.getCharacterEncoding()).andReturn("UTF-8");

或者按照 Péter Török 的建议使用 createNiceMock()。

于 2010-09-17T21:34:05.933 回答