1

以下に示す unittest クラスでは、テストの 1 つが失敗し、もう 1 つが成功します。どちらのテストもCGLIB、インターセプター モックを使用してオブジェクトを作成し、インターセプターが相互作用することを確認しようとします。テストは、CGLIB で動的にサブクラス化されるクラスによって異なります。成功するテストは、プレーンな Java インターフェースをサブクラス化します。失敗したものは、動的サブクラス (で作成Mockito) をサブクラス化します。最初のテストが失敗するのはなぜですか?

ありがとう。

public class ATest {

    @Test
    // This test fails.
    public void cannotCallMethodOnMockWrapper() throws Throwable {
        final Class<? extends Bar> c = Mockito.mock(Bar.class).getClass();
        verifyInterceptorIsInvokedOnCallToObjectOfEnhanced(c);
    }

    @Test
    // This test succeeds.
    public void interceptorIsCalled() throws Throwable {
        final Class<? extends Bar> c = Bar.class;
        verifyInterceptorIsInvokedOnCallToObjectOfEnhanced(c);
    }

    private void verifyInterceptorIsInvokedOnCallToObjectOfEnhanced(
            final Class<? extends Bar> c) throws Throwable {
        final MethodInterceptor interceptor = Mockito.mock(
                MethodInterceptor.class);
        final Bar wrapper = (Bar) Enhancer.create(c, interceptor);

        // Here is where the failing test chokes with exception:
        // NoSuchMethodError
        wrapper.foo();

        verifyInterceptIsCalledOn(interceptor);
    }

    private void verifyInterceptIsCalledOn(
            final MethodInterceptor interceptor) throws Throwable {
        verify(interceptor).intercept(any(), any(Method.class), 
                any(Object[].class), any(MethodProxy.class));
    }

    public static interface Bar {
        void foo();
    }

}

アップデート:

障害のスタック トレース

java.lang.NoSuchMethodError: java.lang.Object.foo()V
    at ATest$Bar$$EnhancerByMockitoWithCGLIB$$2e1d601f.foo(<generated>)
    at ATest.verifyInterceptorIsInvokedOnCallToObjectOfEnhanced(ATest.java:37)
    at ATest.cannotCallMethodOnMockWrapper(ATest.java:19)

また、Mockito モックが CGLIB 拡張クラスであるかどうかについては、以下のテスト (失敗) はそうではないことを示唆しているようです。

public class ATest {

    @Test
    public void mockitoMockIsCGLIBEnhanced() {
        assertTrue(Enhancer.isEnhanced(Mockito.mock(Bar.class).getClass()));
    }

    public static interface Bar {
        void foo();
    }

}
4

1 に答える 1

2

net.sf.cglib.core.CodeGenerationExceptionスタックトレースがない場合は、おそらくInvocationTargetExceptionまたはClassFormatErrorのような原因があると思います。

そしてそれは、CGLIBがそれ自体で作成したすでに拡張されたクラスを拡張できないために発生します。MockitoはJVMの内部でCGLIBを使用しているため、Mockitoクラスを拡張することはできません。元のクラスを拡張する必要があります。さらに、コールバックが生成されたクラスに渡す必要のあるインスタンスである可能性がある場合でも、独自のエンハンサーを作成することにより、上位クラスのMockitoコールバックはありません。そのため、モックモック機能は機能しません。とにかく、私はトピックから分岐しています。

したがって、基本的にCGLIBで拡張クラスを拡張することはできません。モックを渡したために実行時にこの問題が発生する場合は、このようなコードが必要だと思います。拡張されているかどうかを確認し、拡張されている場合はスーパークラス(元のクラス)を使用してください。 /またはインターフェース:

public class CGLIBEnhancerEnhancer implements TypeEnhancer {
    public void Object enhance(Object objectCandidateToEnhance, MethodInterceptor interceptor) {
        Class classCandidateToEnhance = classCandidateToEnhance.getClass();
        if(Enhancer.isEnhanced(classCandidateToEnhance)
           || Mockito.mockingDetails(objectCandidateToEnhance).isMock()) {
            // safe with CGLIB (2.x) enhanced class 
            return (Bar) Enhancer.create(
                    classCandidateToEnhance.getSuperclass(),
                    classCandidateToEnhance.getInterfaces(),
                    interceptor
                );
        } else
            return (Bar) Enhancer.create(classCandidateToEnhance, interceptor);
        }
    }
}

編集:私は与えられた例を実行しました、そしてそれは確かに私にCodeGenerationException、あなたがこの答えの終わりに見ることができることを与えます。環境によっては、投稿したものとは異なるスタックトレースが表示される場合があります。

あなたの例外を考えると、二重拡張クラスがどのように作成されるかについて、実行時に問題が発生する可能性があると思います。スタックトレースは、インスタンスにfooメソッドさえないことを示唆しているため、オブジェクト(実際のモックイトモックのようです)が間違ったタイプから生成されたようです。エンハンサーのAPIを調べて、タイプとインターフェイスを正しく使用して拡張クラスを作成します。またはの新しいインスタンスを使用するEnhancerか、静的メソッドを使用できます。

また、インターセプターを自分で証明していて、特定のインスタンスの動作(モックかどうか)を実行したい場合は、元のオブジェクトへの参照を保持するようにインターセプターを作成する必要があります。

編集2:実際には、技術的な理由により、MockitoはCGLIBを再パッケージ化/ jarjar /インライン化しています。これはnet.sf.cglib.proxy.Enhancer、mockitoモックを検出できないことを意味します。代わりに、1.9.5で新しく導入されたAPIを使用して、Mockito.mockingDetails(instance).isMock()モックモックを検出する必要があります。または、repackaged / jarjared/inlinedを使用しorg.mockito.cglib.proxy.Enhancerます。最終的に、Mockitoモックを検出するには、クラスパスにMockitoが必要になります。

お役に立てば幸い

org.mockito.cglib.core.CodeGenerationException: java.lang.reflect.InvocationTargetException-->null
    at org.mockito.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:238)
    at org.mockito.cglib.proxy.Enhancer.createHelper(Enhancer.java:378)
    at org.mockito.cglib.proxy.Enhancer.create(Enhancer.java:286)
    at org.mockito.cglib.proxy.Enhancer.create(Enhancer.java:664)
    at ATest.verifyInterceptorIsInvokedOnCallToObjectOfEnhanced(ATest.java:32)
    at ATest.cannotCallMethodOnMockWrapper(ATest.java:18)
    ... removed
    at org.junit.runner.JUnitCore.run(JUnitCore.java:157)
    ... removed
Caused by: java.lang.reflect.InvocationTargetException
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at org.mockito.cglib.core.ReflectUtils.defineClass(ReflectUtils.java:385)
    at org.mockito.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:220)
    ... 31 more
Caused by: java.lang.ClassFormatError: Duplicate method name&signature in class file ATest$Bar$$EnhancerByMockitoWithCGLIB$$58a2468b$$EnhancerByCGLIB$$9232d1df
    at java.lang.ClassLoader.defineClass1(Native Method)
    at java.lang.ClassLoader.defineClassCond(ClassLoader.java:631)
    at java.lang.ClassLoader.defineClass(ClassLoader.java:615)
    ... 37 more
于 2012-12-12T10:26:02.677 に答える