mocking final classes?

637 views
Skip to first unread message

Mikael Sundberg

unread,
Apr 22, 2009, 3:25:24 PM4/22/09
to powe...@googlegroups.com

 

Hi, im having some trouble mocking final classes. Or more exact StringBuffer.

Below is a modification of the http://code.google.com/p/powermock/wiki/MockConstructor example (basicly changed from File to StringBuffer).

 

Shouldn’t it be possible to mock StringBuffer? Or am I doing something wrong?

 

 

Error I get is:

Cannot subclass final class class java.lang.StringBuffer

java.lang.IllegalArgumentException: Cannot subclass final class class java.lang.StringBuffer

       at net.sf.cglib.proxy.Enhancer.generateClass(Enhancer.java:446)

        at net.sf.cglib.core.DefaultGeneratorStrategy.generate(DefaultGeneratorStrategy.java:25)

        at net.sf.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:216)

        at net.sf.cglib.proxy.Enhancer.createHelper(Enhancer.java:377)

        at net.sf.cglib.proxy.Enhancer.createClass(Enhancer.java:317)

        at org.powermock.api.easymock.internal.signedsupport.SignedSupportingClassProxyFactory.createProxy(SignedSupportingClassProxyFactory.java:155)

        at org.easymock.internal.MocksControl.createMock(MocksControl.java:40)

        at org.powermock.api.easymock.PowerMock.doMock(PowerMock.java:2089)

        at org.powermock.api.easymock.PowerMock.createMock(PowerMock.java:92)

        at javaapplication32.NewClassTest.testCreateDirectoryStructure_ok(NewClassTest.java:29)

        at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$2.runTestMethod(PowerMockJUnit44RunnerDelegateImpl.java:217)

        at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$2.runBeforesThenTestThenAfters(PowerMockJUnit44RunnerDelegateImpl.java:207)

        at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl.invokeTestMethod(PowerMockJUnit44RunnerDelegateImpl.java:202)

        at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl.runMethods(PowerMockJUnit44RunnerDelegateImpl.java:156)

        at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$1.run(PowerMockJUnit44RunnerDelegateImpl.java:130)

        at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl.run(PowerMockJUnit44RunnerDelegateImpl.java:128)

        at org.powermock.modules.junit4.common.internal.impl.JUnit4TestSuiteChunkerImpl.run(JUnit4TestSuiteChunkerImpl.java:112)

        at org.powermock.modules.junit4.common.internal.impl.AbstractCommonPowerMockRunner.run(AbstractCommonPowerMockRunner.java:44)

 

 

 

/Micke

 

*******************************’

 

public final class NewClass {

            public String createDirectoryStructure(String directoryPath) {

                StringBuffer directory = new StringBuffer(directoryPath);

                   directory.capacity();

                return directory.toString();

        }

}

 

 

import java.io.File;

import org.junit.Test;

import org.junit.runner.RunWith;

 

import org.powermock.core.classloader.annotations.PrepareForTest;

import org.powermock.modules.junit4.PowerMockRunner;

import static org.powermock.api.easymock.PowerMock.*;

 

import static org.easymock.EasyMock.expect;

 

 

 

@RunWith(PowerMockRunner.class)

@PrepareForTest( {StringBuffer.class, NewClass.class} )

public class NewClassTest {

 

        @Test

        public void testCreateDirectoryStructure_ok() throws Exception {

                final String path = "directoryPath";

                StringBuffer stringBufferMock = createMock(StringBuffer.class);

                NewClass tested = new NewClass();

                expectNew(StringBuffer.class, path).andReturn(stringBufferMock);

                expect(stringBufferMock.capacity()).andReturn(1);

                replay(stringBufferMock, File.class);

                tested.createDirectoryStructure(path);

                verify(stringBufferMock, File.class);

        }

}

Johan Haleby

unread,
Apr 23, 2009, 2:45:53 AM4/23/09
to powe...@googlegroups.com
Hi,

First of all you should consider if you really need to mock StringBuffer. It's a Java standard class which doesn't communicate with another environment (such as the file system) so if it's possible I think it's better that you use a normal string and use state verification (i.e. check that the string has been updated according to your expectations) instead of behavior verification (verifying that method X was called on the StringBuffer).

But OK, back to your question. The reason why you cannot mock StringBuffer in your simple example is because it's a final system class. This is of great importance! System classes cannot be byte-code manipulated (without changing your build environment and reverting to static byte-code manipulation) and thus PowerMock cannot remove the final modifier. The reason why File works is because it's not final and PowerMock doesn't have to modify File itself in order to achieve mocking of this class. Since PowerMock 1.2 it's possible to mock static methods in system classes anyway for classes that are not final (and in the next version we have support for final classes as well which is really cool!) but I've never tried if it works when mocking a new instance call for system classes as well. This is definitely very interesting and we should try it out! Please have a look at http://code.google.com/p/powermock/wiki/MockSystem and see if you can work something out from there, it may work already but I'll add an issue to our issue tracker anyways.

/Johan

Mikael Sundberg

unread,
Apr 23, 2009, 4:02:00 AM4/23/09
to powe...@googlegroups.com

Ah your right. I should really use state verification instead. But I can only find how to get the internal state of fields. This is afteral a variable that just lives inside a method, I cant find any way to get a hold of that variable? Im pretty new too mocking so it might be me but still…

 

 

I had a feeling it was since it was a final system class it didn’t work J

 

/Micke

Johan Haleby

unread,
Apr 23, 2009, 12:04:28 PM4/23/09
to powe...@googlegroups.com
You can't get the state of a variable whose life-cycle is limited to a
method invocation since it's created and disposed with the invocation.
You probably don't need access to the actual StringBuffer but rather
just check the outcome of the method with regard to the input parameter.
I cannot give you any more clues since I don't know what your code looks
like, the example that you provided does nothing.

/Johan

Mikael Sundberg wrote:
>
> Ah your right. I should really use state verification instead. But I
> can only find how to get the internal state of fields. This is afteral
> a variable that just lives inside a method, I cant find any way to get
> a hold of that variable? Im pretty new too mocking so it might be me
> but still…
>
> I had a feeling it was since it was a final system class it didn’t work J
>
> /Micke
>
> *From:* powe...@googlegroups.com [mailto:powe...@googlegroups.com]
> *On Behalf Of *Johan Haleby
> *Sent:* den 23 april 2009 08:46
> *To:* powe...@googlegroups.com
> *Subject:* Re: mocking final classes?
>
> Hi,
>
> First of all you should consider if you really need to mock
> StringBuffer. It's a Java standard class which doesn't communicate
> with another environment (such as the file system) so if it's possible
> I think it's better that you use a normal string and use state
> verification (i.e. check that the string has been updated according to
> your expectations) instead of behavior verification (verifying that
> method X was called on the StringBuffer).
>
> But OK, back to your question. The reason why you cannot mock
> StringBuffer in your simple example is because it's a *final system
> class*. This is of great importance! System classes cannot be
> byte-code manipulated (without changing your build environment and
> reverting to static byte-code manipulation) and thus PowerMock cannot
> remove the final modifier. The reason why File works is because it's
> not final and PowerMock doesn't have to modify File itself in order to
> achieve mocking of this class. Since PowerMock 1.2 it's possible to
> mock static methods in system classes anyway for classes that are not
> final (and in the next version we have support for final classes as
> well which is really cool!) but I've never tried if it works when
> mocking a new instance call for system classes as well. This is
> definitely very interesting and we should try it out! Please have a
> look at http://code.google.com/p/powermock/wiki/MockSystem and see if
> you can work something out from there, it /may/ work already but I'll
> add an issue to our issue tracker anyways.
>
> /Johan
>
> On Wed, Apr 22, 2009 at 9:25 PM, Mikael Sundberg
> <Mikael....@artificial-solutions.com
Reply all
Reply to author
Forward
0 new messages