Adyen / adyen-3ds2-android

Apache License 2.0
26 stars 8 forks source link

Unable to Mockito mock ThreeDS2Service or ThreeDS2ServiceInternal #67

Closed kelvinwatson closed 5 months ago

kelvinwatson commented 6 months ago

Describe the bug Since upgrading to com.adyen.threeds:adyen-3ds2:2.2.16, we are no longer able to mock the ThreeDS2Service interface with Mockito. We are getting this error:

To Reproduce Steps to reproduce the behavior:

  1. Upgrade from 2.2.15 to 2.2.16
  2. Run your test that mocks ThreeDS2Service
  3. Observe this error:
org.mockito.exceptions.base.MockitoException: 
Mockito cannot mock this class: interface com.adyen.threeds2.ThreeDS2Service.

If you're not sure why you're getting this error, please report to the mailing list.

Java               : 17
JVM vendor name    : Azul Systems, Inc.
JVM vendor version : 17.0.4.1+1-LTS
JVM name           : OpenJDK 64-Bit Server VM
JVM version        : 17.0.4.1+1-LTS
JVM info           : mixed mode, sharing
OS name            : Mac OS X
OS version         : 12.6.5

You are seeing this disclaimer because Mockito is configured to create inlined mocks.
You can learn about inline mocks and their limitations under item #39 of the Mockito class javadoc.

Underlying exception : org.mockito.exceptions.base.MockitoException: Cannot instrument interface com.adyen.threeds2.ThreeDS2Service because it or one of its supertypes could not be initialized
    at com.example.app.adyen.AdyenThreeDs2ClientTest.beforeEach(AdyenThreeDs2ClientTest.kt:508)
    at java.base@17.0.4.1/java.lang.reflect.Method.invoke(Method.java:568)
    at app//org.robolectric.RobolectricTestRunner$HelperTestRunner$1.evaluate(RobolectricTestRunner.java:588)
    at app//org.robolectric.internal.SandboxTestRunner$2.lambda$evaluate$2(SandboxTestRunner.java:290)
    at app//org.robolectric.internal.bytecode.Sandbox.lambda$runOnMainThread$0(Sandbox.java:101)
    at java.base@17.0.4.1/java.util.concurrent.FutureTask.run(FutureTask.java:264)
    at java.base@17.0.4.1/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136)
    at java.base@17.0.4.1/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635)
    at java.base@17.0.4.1/java.lang.Thread.run(Thread.java:833)
Caused by: org.mockito.exceptions.base.MockitoException: Cannot instrument interface com.adyen.threeds2.ThreeDS2Service because it or one of its supertypes could not be initialized
    at net.bytebuddy.TypeCache.findOrInsert(TypeCache.java:157)
    at net.bytebuddy.TypeCache$WithInlineExpunction.findOrInsert(TypeCache.java:371)
    at net.bytebuddy.TypeCache.findOrInsert(TypeCache.java:179)
    at net.bytebuddy.TypeCache$WithInlineExpunction.findOrInsert(TypeCache.java:382)
    ... 9 more
Caused by: java.lang.RuntimeException: java.lang.NullPointerException: Cannot invoke "java.lang.Class.getDeclaredConstructor(java.lang.Class[])" because "<local30>" is null
    at com.adyen.threeds2.internal.isCompatVectorFromResourcesEnabled.<clinit>(Unknown Source)
    at com.adyen.threeds2.internal.ThreeDS2ServiceInternal.<clinit>(:26)
    at com.adyen.threeds2.ThreeDS2Service.<clinit>(:45)
    at java.base/java.lang.Class.forName0(Native Method)
    at java.base/java.lang.Class.forName(Class.java:467)
    at org.mockito.internal.creation.bytebuddy.InlineBytecodeGenerator.assureInitialization(InlineBytecodeGenerator.java:231)
    at org.mockito.internal.creation.bytebuddy.InlineBytecodeGenerator.triggerRetransformation(InlineBytecodeGenerator.java:256)
    at org.mockito.internal.creation.bytebuddy.InlineBytecodeGenerator.mockClass(InlineBytecodeGenerator.java:213)
    at org.mockito.internal.creation.bytebuddy.TypeCachingBytecodeGenerator.lambda$mockClass$0(TypeCachingBytecodeGenerator.java:47)
    at net.bytebuddy.TypeCache.findOrInsert(TypeCache.java:157)
    at net.bytebuddy.TypeCache$WithInlineExpunction.findOrInsert(TypeCache.java:371)
    at net.bytebuddy.TypeCache.findOrInsert(TypeCache.java:179)
    at net.bytebuddy.TypeCache$WithInlineExpunction.findOrInsert(TypeCache.java:382)
    at org.mockito.internal.creation.bytebuddy.TypeCachingBytecodeGenerator.mockClass(TypeCachingBytecodeGenerator.java:40)
    at org.mockito.internal.creation.bytebuddy.InlineDelegateByteBuddyMockMaker.createMockType(InlineDelegateByteBuddyMockMaker.java:389)
    at org.mockito.internal.creation.bytebuddy.InlineDelegateByteBuddyMockMaker.doCreateMock(InlineDelegateByteBuddyMockMaker.java:349)
    at org.mockito.internal.creation.bytebuddy.InlineDelegateByteBuddyMockMaker.createMock(InlineDelegateByteBuddyMockMaker.java:328)
    at org.mockito.internal.creation.bytebuddy.InlineByteBuddyMockMaker.createMock(InlineByteBuddyMockMaker.java:56)
    at org.mockito.internal.util.MockUtil.createMock(MockUtil.java:53)
    at org.mockito.internal.MockitoCore.mock(MockitoCore.java:96)
    at org.mockito.Mockito.mock(Mockito.java:1965)
    at com.example.app.adyen.AdyenThreeDs2ClientTest.beforeEach(AdyenThreeDs2ClientTest.kt:508)
    at java.base/java.lang.reflect.Method.invoke(Method.java:568)
    at org.mockito.internal.junit.JUnitSessionStore$1.evaluateSafely(JUnitSessionStore.java:55)
    at org.mockito.internal.junit.JUnitSessionStore$1.evaluate(JUnitSessionStore.java:43)
    ... 7 more
Caused by: java.lang.NullPointerException: Cannot invoke "java.lang.Class.getDeclaredConstructor(java.lang.Class[])" because "<local30>" is null
    ... 32 more

Expected behavior We should be able to mock the ThreeDS2Service interface.

Desktop (please complete the following information):

kelvinwatson commented 6 months ago

Workaround: Creating this stub implementation rather than a Mockito mock seems to work for now.

class Stub: ThreeDS2Service {
        override fun initialize(
            p0: Context?,
            p1: ConfigParameters?,
            p2: String?,
            p3: UiCustomization?
        ): InitializeResult {
            TODO("Not yet implemented")
        }

        override fun createTransaction(p0: String?, p1: String): TransactionResult {
            TODO("Not yet implemented")
        }

        override fun cleanup(p0: Context?) {
            TODO("Not yet implemented")
        }

        override fun getSDKVersion(): String {
            TODO("Not yet implemented")
        }

        override fun getWarnings(): MutableList<Warning> {
            TODO("Not yet implemented")
        }

    }
tkuntubayev commented 5 months ago

Hi @kelvinwatson, Thank you for reaching out and providing all the details. We will investigate further and let you know if there's something can be changed from our side to make it compatible with Mockito.

fdcb commented 5 months ago

Hey @kelvinwatson, this issue happens due to our newest security measures. Unfortunately we can't find a way to make this work with Mockito for now. Since there's an easy work around by implementing a fake ThreeDS2Service we will close this issue now and if we ever find a fix for this we will update you.