我正在使用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()
方法对请求和响应模拟所做的方法调用?
答案 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个要求:
@RunWith(PowerMockRunner.class)
在类的级别注释
测试案例。@PrepareForTest(PortletContextFactory.class)
注释。答案 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
,所以你只能测试这里的问题:你写的代码。