Menu

#17 IllegalAccessError for Package-Scope-Interfaces

EasyMock_2.4
open
EasyMock (40)
5
2012-10-05
2009-03-24
Virtualwarp
No

When you declare a Interface to be accessible only inside a package and setup a proxy in a test using a parallel package hierarchy, the proxy throws a IllegalAccessError on the first call:

java.lang.IllegalAccessError: tried to access class plugin.operation.cleanup.IElementCleanup from class $Proxy2
at $Proxy2.createCleanup(Unknown Source)
at ModelCleanUp.cleanupAndValidate(ModelCleanUp.java:35)
at plugin.operation.cleanup.ModelCleanUpTest.test_validateAndCleanup(ModelCleanUpTest.java:56)
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:585)
at junit.framework.TestCase.runTest(TestCase.java:168)
at junit.framework.TestCase.runBare(TestCase.java:134)
at junit.framework.TestResult$1.protect(TestResult.java:110)
at junit.framework.TestResult.runProtected(TestResult.java:128)
at junit.framework.TestResult.run(TestResult.java:113)
at junit.framework.TestCase.run(TestCase.java:124)
at junit.framework.TestSuite.runTest(TestSuite.java:232)
at junit.framework.TestSuite.run(TestSuite.java:227)
at org.junit.internal.runners.OldTestClassRunner.run(OldTestClassRunner.java:76)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:45)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:460)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:673)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:386)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:196)

interface IElementCleanup {
public boolean createCleanup(IElement element);
}

@Test
@Test
public void test_validateAndCleanup() {
IElement element1 = fControl.createMock("element1", IElement.class);
IElementCleanup cleanup = fControl.createMock("cleanup", IElementCleanup.class);
fControl.replay();
ModelCleanUp.cleanupAndValidate(cleanup1, element1, element2, element3);
fControl.verify();
}

Discussion

  • Henri Tremblay

    Henri Tremblay - 2009-03-28

    I'm not sure how you did that. I made a little test. The mocked interface is package scope. The test is in the same package. I pass nicely.

    interface IElementCleanup {
    public boolean createCleanup();
    }

    public class ModelCleanup {
    void cleanupAndValidate(IElementCleanup e) {
    e.createCleanup();
    }
    }

    public class PackageScope {
    @Test
    public void test_validateAndCleanup() {
    IElementCleanup i = createMock(IElementCleanup.class);
    expect(i.createCleanup()).andReturn(true);
    replay(i);

        ModelCleanup c = new ModelCleanup();
        c.cleanupAndValidate(i);
    
        verify(i);
    }
    

    }

    Can you show me how to make this test case fail?

     
  • Virtualwarp

    Virtualwarp - 2009-03-30

    Sorry for the incorrect posting, i missed that the exeception occurs only with together with andReturn. Here is an testcase which should be green:

    package de.testpackage;

    import static org.easymock.EasyMock.*;
    import junit.framework.TestCase;

    import org.easymock.IMocksControl;
    import org.junit.Test;

    public class FactoryMockTest extends TestCase {
    public static interface IFactory {
    void call(IPackagePrivate packagePrivateCall);

    void call(IPublic publicCall);
    
    IPackagePrivate createPackagePrivate();
    
    IPublic createPublic();
    

    }

    public static interface IPublic {

    public void call();
    

    }

    static interface IPackagePrivate {

    public void call();
    

    }

    @Test
    public void test_call_ok() {
    IMocksControl control = createStrictControl();
    IPackagePrivate packagePrivateMock = control.createMock(IPackagePrivate.class);
    IPublic publicMock = control.createMock(IPublic.class);
    IFactory factoryMock = control.createMock(IFactory.class);

    expect(factoryMock.createPublic()).andReturn(publicMock);
    factoryMock.call(publicMock);
    factoryMock.call(packagePrivateMock);
    
    control.replay();
    
    assertEquals(publicMock, factoryMock.createPublic());
    factoryMock.call(publicMock);
    factoryMock.call(packagePrivateMock);
    
    control.verify();
    

    }

    @Test
    public void test_call_fails() {
    IMocksControl control = createStrictControl();
    IPackagePrivate packagePrivateMock = control.createMock(IPackagePrivate.class);
    IFactory factoryMock = control.createMock(IFactory.class);

    expect(factoryMock.createPackagePrivate()).andReturn(packagePrivateMock);
    
    control.replay();
    assertEquals(packagePrivateMock, factoryMock.createPackagePrivate());
    
    control.verify();
    

    }
    }

     
  • Henri Tremblay

    Henri Tremblay - 2009-04-12

    This is tricky. It's a JDK problem. A proxy is created in the default package. So if you return a package scope interface from a proxy, you get this error. The case is quite rare, Things I can do:

    1- Just have a nice error message about the limitation
    2- Dynamically create and interface bytecode in the same package and package scope. Huge workaround to the JDK and a little bit complex for such a rare case
    3- It's working with cglib so creating the new proxy factory would work. Like this:

    public class MyMocksControl extends MocksControl {

    private static final long serialVersionUID = 5066517411023500987L;
    
    public MyMocksControl(MockType type) {
        super(type);
    }
    
    @SuppressWarnings("unchecked")
    @Override
    protected <T> IProxyFactory<T> createProxyFactory(Class<T> toMock) {
        return new IProxyFactory() {
            @Override
            public Object createProxy(Class toMock,
                    final InvocationHandler handler) {
                return Enhancer.create(toMock,
                        new net.sf.cglib.proxy.InvocationHandler() {
                            @Override
                            public Object invoke(Object proxy, Method method,
                                    Object[] args) throws Throwable {
                                return handler.invoke(proxy, method, args);
                            }
    
                        });
            }
        };
    }
    

    }

    And the test:

    @Test
    public void testCglib() throws Exception {
        MyMocksControl c = new MyMocksControl(MockType.STRICT);
        IFactory f = c.createMock(IFactory.class);
        f.createPackagePrivate();
    }
    

    You can use this workaround. I'll try to fix this one but not in the next release.

     

Log in to post a comment.