How to turn off recording for an EasyMock object?
I am testing a servlet's doPost()
method using EasyMock objects for the HttpServletRequest
and HttpServletResponse
arguments. Within the doPost()
method I'm testing the request and response objects are used as arguments to a static method class for another class, and I want to disregard (i.e. not record as expected) any calls made on the request and response objects within this method call (it's not relevant to this test anyway). For example the doPost()
method of the servlet class I'm testing looks like this:
@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);
}
When the above code gets to the step where PortletContextFactory.createPortletContext()
is called I don't really care what method calls are made on the request and response objects within that method, but if I pass in mock request and response objects when testing this method I get errors from EasyMock telling me that there are missing behavior definitions. For example I have a test method which looks like this:
@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);
}
I get the following error when I run the test class:
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.eclips开发者_如何学Pythone.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
I assume that the errors above are caused by not recording the method calls executed within the PortletContextFactory.createPortletContext()
method on the mock request and response objects passed in as arguments. If this is actually what's happening here then how can I rework things so that the method calls made on the request and response mocks by the PortletContextFactory.createPortletContext()
method are disregarded?
Try Mockito http://mockito.org/.
It is much easier to use from EasyMock, and doesn't force you to code all the method calls.
Perhaps you need something like this:
expect(mockServletRequest.getCharacterEncoding()).andReturn("UTF-8");
Or use createNiceMock() as Péter Török suggests.
You need to mock PortletContextFactory.createPortletContext
call. EasyMock itself does not support static method mocking, however PowerMock extension for EasyMock does. Here is the sample code you should insert into your test:
mockStatic(PortletContextFactory.class);
expect(PortletContextFactory.createPortletContext(mockServletRequest, mockServletResponse)).andReturn(mockPortletContext);
replay(PortletContextFactory.class);
There are also 2 requirements:
- Use the
@RunWith(PowerMockRunner.class)
annotation at the class-level of the test case. - Use the
@PrepareForTest(PortletContextFactory.class)
annotation at the class-level of the test case.
Read more at: http://code.google.com/p/powermock/wiki/MockStatic
Alternative approach to test in similar scenarios :
class Class_Under_Test {
public void A() {
B b = C.create(); //create is static
D d = e.(b);
}
}
To get around the static reference problem in EasyMock you can change your method definition to :
@VisibleForTesting
B create() {
return C.create();
}
public A() {
B b = create();
D d = e.(b);
}
In your test class you can do this :
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);
}
}
All your further tests will use mockObject to call methods of the Class_Under_Test.
To turn of the recording, use a nice mock. From the documentation :
On a Mock Object returned by
createMock()
the default behavior for all methods is to throw anAssertionError
for all unexpected method calls. If you would like a "nice" Mock Object that by default allows all method calls and returns appropriate empty values (0, null or false), usecreateNiceMock()
instead.
Also, more generally, what you want to mock is createPortletContext(servletRequest, servletResponse)
. Unfortunately that's a static call.
To achieve this mock, create you own factory that will return the portletContext
, and pass this factory to you tested class (preferably in the constructor).
Mock this factory and the portletContext
too, so you can test only what matter here : the code you wrote.
精彩评论