PowerMock StackOveflowError on ArrayListMultimap



EDIT:这现在被归档为Powermock的问题:http://code.google.com/p/powermock/issues/detail?id=449&thanks=449&ts=1371519268

我正在使用EasyMock测试一些代码,它调用一个返回ArrayListMultimap的方法,我不想去构造一个充满模拟的集合对象,所以我决定简单地模拟ArrayListMultimap,并使它以标准模拟对象的方式返回我想要的任何模拟。ArrayListMultimap是final,所以我在它上面撒了一些PowerMock的粉末。然而,当我运行测试时,我得到:

java.lang.StackOverflowError
    at java.lang.reflect.Method.copy(Method.java:143)
    at java.lang.reflect.ReflectAccess.copyMethod(ReflectAccess.java:118)
    at sun.reflect.ReflectionFactory.copyMethod(ReflectionFactory.java:282)
    at java.lang.Class.copyMethods(Class.java:2757)
    at java.lang.Class.getDeclaredMethods(Class.java:1793)
    at org.easymock.internal.BridgeMethodResolver.getAllDeclaredMethods(BridgeMethodResolver.java:434)
    at org.easymock.internal.BridgeMethodResolver.findBridgedMethod(BridgeMethodResolver.java:78)
    at org.easymock.internal.ClassProxyFactory$MockMethodInterceptor.intercept(ClassProxyFactory.java:87)
    at com.google.common.collect.ArrayListMultimap$$EnhancerByCGLIB$$2dd82dd1.equals(<generated>)
    at org.easymock.internal.ExpectedInvocation.matches(ExpectedInvocation.java:85)
    at org.easymock.internal.UnorderedBehavior.addActual(UnorderedBehavior.java:57)
    at org.easymock.internal.MocksBehavior.addActual(MocksBehavior.java:87)
    at org.easymock.internal.ReplayState.invokeInner(ReplayState.java:58)
    at org.easymock.internal.ReplayState.invoke(ReplayState.java:46)
    at org.easymock.internal.MockInvocationHandler.invoke(MockInvocationHandler.java:40)
    at org.easymock.internal.ObjectMethodsFilter.invoke(ObjectMethodsFilter.java:85)
    at org.easymock.internal.ClassProxyFactory$MockMethodInterceptor.intercept(ClassProxyFactory.java:94)
    at com.google.common.collect.ArrayListMultimap$$EnhancerByCGLIB$$2dd82dd1.equals(<generated>)
    at org.easymock.internal.ExpectedInvocation.matches(ExpectedInvocation.java:85)
    at org.easymock.internal.UnorderedBehavior.addActual(UnorderedBehavior.java:57)
    at org.easymock.internal.MocksBehavior.addActual(MocksBehavior.java:87)
    at org.easymock.internal.ReplayState.invokeInner(ReplayState.java:58)
    at org.easymock.internal.ReplayState.invoke(ReplayState.java:46)
    at org.easymock.internal.MockInvocationHandler.invoke(MockInvocationHandler.java:40)
    at org.easymock.internal.ObjectMethodsFilter.invoke(ObjectMethodsFilter.java:85)
    at org.easymock.internal.ClassProxyFactory$MockMethodInterceptor.intercept(ClassProxyFactory.java:94)
    at com.google.common.collect.ArrayListMultimap$$EnhancerByCGLIB$$2dd82dd1.equals(<generated>)
    at org.easymock.internal.ExpectedInvocation.matches(ExpectedInvocation.java:85)
    at org.easymock.internal.UnorderedBehavior.addActual(UnorderedBehavior.java:57)
    at org.easymock.internal.MocksBehavior.addActual(MocksBehavior.java:87)
    at org.easymock.internal.ReplayState.invokeInner(ReplayState.java:58)
    at org.easymock.internal.ReplayState.invoke(ReplayState.java:46)
    at org.easymock.internal.MockInvocationHandler.invoke(MockInvocationHandler.java:40)
    at org.easymock.internal.ObjectMethodsFilter.invoke(ObjectMethodsFilter.java:85)
    at org.easymock.internal.ClassProxyFactory$MockMethodInterceptor.intercept(ClassProxyFactory.java:94)
    at com.google.common.collect.ArrayListMultimap$$EnhancerByCGLIB$$2dd82dd1.equals(<generated>)
    at org.easymock.internal.ExpectedInvocation.matches(ExpectedInvocation.java:85)
    at org.easymock.internal.UnorderedBehavior.addActual(UnorderedBehavior.java:57)
    at org.easymock.internal.MocksBehavior.addActual(MocksBehavior.java:87)
    at org.easymock.internal.ReplayState.invokeInner(ReplayState.java:58)
    at org.easymock.internal.ReplayState.invoke(ReplayState.java:46)
    at org.easymock.internal.MockInvocationHandler.invoke(MockInvocationHandler.java:40)
    at org.easymock.internal.ObjectMethodsFilter.invoke(ObjectMethodsFilter.java:85)
    at org.easymock.internal.ClassProxyFactory$MockMethodInterceptor.intercept(ClassProxyFactory.java:94)
    at com.google.common.collect.ArrayListMultimap$$EnhancerByCGLIB$$2dd82dd1.equals(<generated>)
    at org.easymock.internal.ExpectedInvocation.matches(ExpectedInvocation.java:85)
    at org.easymock.internal.UnorderedBehavior.addActual(UnorderedBehavior.java:57)

最后我把这个问题提炼成这个例子:

import com.google.common.collect.ArrayListMultimap;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.api.easymock.PowerMock;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import static org.easymock.EasyMock.expect;
@RunWith(PowerMockRunner.class)
@PrepareForTest(ArrayListMultimap.class)
public class PurePowermockTest {
  @Test
  public void testPowerMockVsGuava() {
    ArrayListMultimap map = PowerMock.createMock(ArrayListMultimap.class);
    expect(map.put("foo", "bar")).andReturn(true);
    PowerMock.replay(map);
    map.put("foo", "bar");  // SOError!
  }
}

上面的例子当然没有测试任何东西,map.put()调用通常会在我正在测试的某个方法中。这段代码只是为了尽可能简洁地演示这个问题。我也知道,我可以只是建立ArrayListMultiMap并返回它,但抛开这一点,嘲笑地图应该工作了。我很确定这是powermock中的一个bug,但我的问题是:

我是否正确使用PowerMock ?这是否可以工作,或者我是否错过了PowerMock的功能或正确使用方法?我用的是EasyMock。方法,但我没有看到PowerMock上的等效方法,所以我认为这是可以的…

对我来说似乎是PowerMock(或用于字节码操作的javassist)中的一个bug。因为我使用PowerMock与Mockito(即PowerMockito)我已经检查了它是否与Mockito可重复-它是。给定测试:

@PrepareForTest(ArrayListMultimap.class)
public class PowerMockitoTest {
  @Rule // used instead @RunWith(PowerMockRunner.class) in newer version of JUnit
  public PowerMockRule rule = new PowerMockRule();
  @Test
  public void testPowerMockitoVsGuava() {
    final ArrayListMultimap<String, String> mock =
        PowerMockito.mock(ArrayListMultimap.class);
    PowerMockito.when(mock.put("foo", "bar")).thenReturn(true);
    Assert.assertTrue(mock.put("foo", "bar")); // SOError!
  }
}

它仍然产生SO,并指向代理的ArrayListMultimap的类(在stacktrace中at com.google.common.collect.ArrayListMultimap$$EnhancerByCGLIB$$2dd82dd1.equals(<generated>))中的等号。

这个特定的bug可能与反复出现的问题88有关-它提到了SO错误,当equals是final(但在ArrayListMultimap中它不是…)或在其中使用getClass()(它没有,另一方面使用instanceof)或从equals调用另一个方法(这可能是一个案例,因为asMap()AbstractMultimap#equals中被调用)。另一方面,我检查了LinkedListMultimap,它可以很好地与PowerMock一起工作,所以它可能是ArrayListMultimap类型层次结构的东西(扩展AbstractMultimap -> AbstractMapBasedMultimap -> AbstractListMultimap,而LinkedListMultimap没有)。

不幸的是,我不知道PowerMock的内部原理,也没有找到任何具体的东西,所以你应该联系PowerMock的开发人员,可能通过谷歌组。


回到你的问题-如果你可以改变你的方法返回ListMultimap,那么你很好-你应该操作接口,而不是具体的实现(你甚至不需要使用PowerMock)。LinkedListMultimap也是一个选项

相关内容

  • 没有找到相关文章

最新更新