2

編集:これは現在、Powermock の問題としてここに提出されています: http://code.google.com/p/powermock/issues/detail?id=449&thanks=449&ts=1371519268

ArrayListMultimap を返すメソッドを呼び出す EasyMock を使用していくつかのコードをテストしていましたが、モックでいっぱいのコレクション オブジェクトを構築する手間をかけたくなかったので、単に ArrayListMultimap をモックして、私が作成したモックを返すようにすることにしました。標準的なモック オブジェクトの機能で必要です。ArrayListMultimap は最終的なものであることが判明したので、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のバグだと確信していますが、私の質問は次のとおりです。

PowerMock を正しく使用していますか? これは機能するはずですか、それとも PowerMock の機能や適切な使用法について見逃しているものはありますか? 私は EasyMock.expect メソッドを使用していますが、PowerMock に同等のものが表示されないため、これで問題ないと思います...

4

1 に答える 1

1

私にはPowerMock(またはバイトコード操作に使用されるjavassist)のバグのようです。私は 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のクラス(at com.google.common.collect.ArrayListMultimap$$EnhancerByCGLIB$$2dd82dd1.equals(<generated>)スタックトレース内)のequalsを指します。

この特定のバグは、繰り返し発生する問題 88に関連している可能性があります- equals が final である場合 (ただし ArrayListMultimap ではそうではありません...)、または getClass() を使用している場合 (そうではなく、instanceof を使用している場合) に SO エラーが言及されています。 ) または equals から別のメソッドを呼び出します (asMap()は内部で呼び出されるため、ここではケースになる可能性がありますAbstractMultimap#equals)。一方、LinkedListMultimapどちらが PowerMock で正常に動作するかを確認したので、ArrayListMultimap型階層 (extends AbstractMultimap-> AbstractMapBasedMultimap-> AbstractListMultimapwhile LinkedListMultimapdoes not) を持つものである可能性があります。

残念ながら、私は PowerMock の内部構造を知らず、具体的なものも何も見つけていません。そのため、おそらくGoogle Group経由で PowerMock 開発者に連絡する必要があります。


問題に戻ります-メソッドを returnListMultimapに変更できる場合は、問題ありません-とにかく具体的な実装ではなく、インターフェースで操作する必要があります(PowerMockを使用する必要さえありません)。LinkedListMultimapここでもオプションです。

于 2013-06-18T14:58:41.217 に答える