You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Please see the attached small demo project with 5 test classes that demonstrate the problem and workaround(s).
My environment is the following:
Windows 7 Enterprise 64-bit
JDK 1.6.0_22
Maven 2.2.1
PowerMock 1.4.6
TestNG 5.14.1
SampleServlet represent the class being tested with very simple doGet method that writes "out" string to response writer.
Consider the SampleServletTestWithoutPrepare test that works fine and doesn't use any PowerMock specific extras except of 'createMock', 'replay' and 'verify'.
Once I add @PrepareForTest annotation to the test class (see SampleServletTestWithPrepare1) test fails with the following exception:
java.lang.AssertionError:
Unexpected method call write("out"):
write("out"): expected: 1, actual: 0
at org.easymock.internal.MockInvocationHandler.invoke(MockInvocationHandler.java:45)
at org.easymock.internal.ObjectMethodsFilter.invoke(ObjectMethodsFilter.java:73)
at org.easymock.internal.ClassProxyFactory$MockMethodInterceptor.intercept(ClassProxyFactory.java:92)
at $java.io.PrintWriter$$EnhancerByCGLIB$$8cbc17d1.write()
at com.volodymyrtsukur.powermock.demos.SampleServlet.doGet(SampleServlet.java:16)
at com.volodymyrtsukur.powermock.demos.SampleServletTestWithPrepare1.doGet(SampleServletTestWithPrepare1.java:37)
at com.volodymyrtsukur.powermock.demos.SampleServletTestWithPrepare1_$$_javassist_0.d1doGet(SampleServletTestWithPrepare1$$javassist_0.java)
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.powermock.modules.testng.internal.PowerMockTestNGMethodHandler.invoke(PowerMockTestNGMethodHandler.java:51)
at com.volodymyrtsukur.powermock.demos.SampleServletTestWithPrepare1$$javassist_0.doGet(SampleServletTestWithPrepare1$$_javassist_0.java)
Call to the 'write' is intercepted by EasyMock , not by PowerMock's gateway. Is it fine?
It may seem useless to specify @PrepareForTest without actually mocking statics or finals. In original test case (in the project that cannot be posted here) instances of classes with final methods were mocked. I strip this down from examples because it actually doesn't have anything to do with the actual problem. I just add @PrepareForTest, specify ObjectFactory and test code doesn't work.
Adding specific class to @PrepareForTest doesn't help (see SampleServletTestWithPrepare2 test) unless I specify the class that is actually the caller of 'write' method, i.e. SampleServlet class (see SampleServletTestWithTargetClassPrepare test). And pay attention that once I inline the servlet code into the test method it starts to pass as well (SampleServletTestWithInline).
So, it seems that each class that calls mock needs to be 'prepared'. I've seen similar rule in the documentation for expectNew but not for regular mocks that just work inside the test with annotated @PrepareForTest and PowerMockObjectFactory. Is it a problem?
In debug it may be seen that test class is prepared as well, that's why inlining the servlet code works.
Once again a big thanks for your detailed description and excellent example project! I can totally understand that what you see doesn't seem logical but it is :). I'll try to answer your questions:
Q. Why do you get a difference with @PrepareForTest when you don't need to prepare anything?
A. Actually you do prepare something when using @PrepareForTest even though you haven't specified anything. The "test case" is always automatically prepared for test when you use the @PrepareForTest annotation. The reason is that when mocking system classes you'd like to keep the same mocking syntax (read about it here: http://blog.jayway.com/2009/05/17/mocking-static-methods-in-java-system-classes/ ).
Q: Call to the 'write' is intercepted by EasyMock , not by PowerMock's gateway. Is it fine?
A: The problem is that the PrintWriter is a Java System class. PowerMock treats system classes specially because if the class would have been final there's no way for PowerMock to byte-code manipulate it (refer to the blog for info). For these classes you need to prepare the class calling the system class instead of the actual system class (see https://code.google.com/p/powermock/wiki/MockSystem ). In your case it becomes quite strange though since even if PrintWriter is a system class it is NOT final and the method you're trying to mock is not final either so it should be possible to mock it normally. How ever in the byte code only checks that the class starts with "java." (i.e. that it's a system class) and doesn't care if the class is final or that the method is final. I'll try to change this and see if we run into any implications. But right now you would have to prepare the class SampleServlet.class for test in SampleServletTestWithPrepare1 and SampleServletTestWithPrepare2 in order to make the test pass. This is because it is SampleServlet that calls the write method (which is located in a java system class).
From flush...@gmail.com on December 16, 2010 21:05:32
Please see the attached small demo project with 5 test classes that demonstrate the problem and workaround(s).
My environment is the following:
Windows 7 Enterprise 64-bit
JDK 1.6.0_22
Maven 2.2.1
PowerMock 1.4.6
TestNG 5.14.1
SampleServlet represent the class being tested with very simple doGet method that writes "out" string to response writer.
Consider the SampleServletTestWithoutPrepare test that works fine and doesn't use any PowerMock specific extras except of 'createMock', 'replay' and 'verify'.
Once I add @PrepareForTest annotation to the test class (see SampleServletTestWithPrepare1) test fails with the following exception:
java.lang.AssertionError:$java.io.PrintWriter$ $EnhancerByCGLIB$$8cbc17d1.write()
Unexpected method call write("out"):
write("out"): expected: 1, actual: 0
at org.easymock.internal.MockInvocationHandler.invoke(MockInvocationHandler.java:45)
at org.easymock.internal.ObjectMethodsFilter.invoke(ObjectMethodsFilter.java:73)
at org.easymock.internal.ClassProxyFactory$MockMethodInterceptor.intercept(ClassProxyFactory.java:92)
at
at com.volodymyrtsukur.powermock.demos.SampleServlet.doGet(SampleServlet.java:16)
at com.volodymyrtsukur.powermock.demos.SampleServletTestWithPrepare1.doGet(SampleServletTestWithPrepare1.java:37)
at com.volodymyrtsukur.powermock.demos.SampleServletTestWithPrepare1_$$_javassist_0.d1doGet(SampleServletTestWithPrepare1$$javassist_0.java)
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.powermock.modules.testng.internal.PowerMockTestNGMethodHandler.invoke(PowerMockTestNGMethodHandler.java:51)
at com.volodymyrtsukur.powermock.demos.SampleServletTestWithPrepare1$$javassist_0.doGet(SampleServletTestWithPrepare1$$_javassist_0.java)
Call to the 'write' is intercepted by EasyMock , not by PowerMock's gateway. Is it fine?
It may seem useless to specify @PrepareForTest without actually mocking statics or finals. In original test case (in the project that cannot be posted here) instances of classes with final methods were mocked. I strip this down from examples because it actually doesn't have anything to do with the actual problem. I just add @PrepareForTest, specify ObjectFactory and test code doesn't work.
Adding specific class to @PrepareForTest doesn't help (see SampleServletTestWithPrepare2 test) unless I specify the class that is actually the caller of 'write' method, i.e. SampleServlet class (see SampleServletTestWithTargetClassPrepare test). And pay attention that once I inline the servlet code into the test method it starts to pass as well (SampleServletTestWithInline).
So, it seems that each class that calls mock needs to be 'prepared'. I've seen similar rule in the documentation for expectNew but not for regular mocks that just work inside the test with annotated @PrepareForTest and PowerMockObjectFactory. Is it a problem?
In debug it may be seen that test class is prepared as well, that's why inlining the servlet code works.
Attachment: invalid-mock-interception.zip
Original issue: http://code.google.com/p/powermock/issues/detail?id=300
The text was updated successfully, but these errors were encountered: