如何关闭EasyMock对象的录制?

时间:2010-09-17 21:14:04

标签: java unit-testing mocking easymock

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

@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()方法对请求和响应模拟所做的方法调用?

5 个答案:

答案 0 :(得分:2)

尝试Mockito http://mockito.org/

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

答案 1 :(得分:1)

也许你需要这样的东西:

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

或者像PéterTörök建议的那样使用createNiceMock()。

答案 2 :(得分: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)注释。
  3. 阅读详情:http://code.google.com/p/powermock/wiki/MockStatic

答案 3 :(得分: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的方法。

答案 4 :(得分:1)

要转动录音,请使用漂亮的模拟器。来自文档:

  

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

另外,更一般地说,你要模拟的是createPortletContext(servletRequest, servletResponse)。不幸的是,这是一个静态的电话。 要实现此模拟,请创建自己的工厂,它将返回portletContext,并将此工厂传递给您测试的类(最好在构造函数中)。 模拟这个工厂和portletContext,所以你只能测试这里的问题:你写的代码。