mockk: Bug: MockKAgentException when creating a mock on >= Api level 28 due to missing 64 binaries

Expected Behavior

Expecting mocked instances to be initialised correctly.

Current Behavior

Currently, whenever a mock of a class is created a MockKAgentException is thrown (see stacktrace below) on api level 28 or greater. I don’t seem to be having any issues on lower API levels.

Steps to Reproduce

  1. Create a test case containing code to create a mock
  2. Run test

Context

  • MockK version: 1.9.3
  • OS: >= API level 28
  • Kotlin version: 1.3.11
  • JDK version: 8
  • JUnit version: 4.12
  • Type of test: instrumented test

Stack trace

Caused by: io.mockk.proxy.MockKAgentException: MockK could not self-attach a jvmti agent to the current VM. This feature is required for inline mocking.
This error occured due to an I/O error during the creation of this agent: java.io.IOException: No such file or directory

Potentially, the current VM does not support the jvmti API correctly
at io.mockk.proxy.android.AndroidMockKAgentFactory.init(AndroidMockKAgentFactory.kt:63)
at io.mockk.impl.JvmMockKGateway.<init>(JvmMockKGateway.kt:46)
at io.mockk.impl.JvmMockKGateway.<clinit>(JvmMockKGateway.kt:172)
... 33 more
Caused by: java.io.IOException: No such file or directory
at java.io.UnixFileSystem.createFileExclusively0(Native Method)
at java.io.UnixFileSystem.createFileExclusively(UnixFileSystem.java:281)
at java.io.File.createTempFile(File.java:2018)
at java.io.File.createTempFile(File.java:2064)
at io.mockk.proxy.android.JvmtiAgent.appendToBootstrapClassLoaderSearch(JvmtiAgent.kt:53)
at io.mockk.proxy.android.AndroidMockKAgentFactory.init(AndroidMockKAgentFactory.kt:48)
... 35 more

Minimal reproducible code (the gist of this issue)

// -----------------------[ GRADLE DEFINITIONS ] -----------------------
dependencies {
    androidTestImplementation "io.mockk:mockk-android:1.9.3"
}
// -----------------------[ YOUR CODE STARTS HERE ] -----------------------
object MockHelper {
    fun mockIsEnabled(enabled: Boolean = false) {

        val mock: LocationChecker = mockk(relaxed = true) //failure here

        val component = DaggerTestAppComponent
            .builder()
            .testModule(TestModule(locationChecker = mock))
            .build()

        val application = Application.getApplication(InstrumentationRegistry.getTargetContext())
        application.appComponent = component
    }
}

@RunWith(AndroidJUnit4::class)
class ScreenTest : BaseUITest() {

    @Before
    fun setUp() {
        MockHelper.mockIsEnabled(true)
    }
    
}

// -----------------------[ YOUR CODE ENDS HERE ] -----------------------

About this issue

  • Original URL
  • State: closed
  • Created 5 years ago
  • Reactions: 24
  • Comments: 48 (1 by maintainers)

Commits related to this issue

Most upvoted comments

I had the same issue in our project while upgrading to the latest gradle version.

At first the https://github.com/mockk/mockk/issues/297#issuecomment-641361770 did not work for us.

By chance I found this warning when using the custom Manifest (https://github.com/mockk/mockk/issues/297#issuecomment-641361770) on the “old” gradle version: PackagingOptions.jniLibs.useLegacyPackaging should be set to true because android:extractNativeLibs is set to "true" in AndroidManifest.xml.

Enabling the PackagingOption within the Android testOptions then finally solved the issue for me:

android {
  testOptions {
      packagingOptions {
          jniLibs {
              useLegacyPackaging true
          }
      }
   }
}

Maybe this workaround helps others 😃

I tried it again and found that I put the flag “extractNativeLibs” into the main manifest instead of the androidTest manifest which has no effect.

It works if you put an AndroidManifest.xml into your src/androidTest folder with this content:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.package">

    <application android:extractNativeLibs="true" />
</manifest>

Of course this seems to only be a workaround for that change, however, but at least it works for now: https://developer.android.com/studio/releases/gradle-plugin#extractNativeLibs

I am still facing this issue and workarounds mentioned above including https://github.com/mockk/mockk/issues/297#issuecomment-901924678 and not working.

@kjaspinder Just switched from 1.12.4 to 1.12.3 after seeing this comment. Worked for me. None of the previous workarounds did the trick either.

@oleksiyp Can you please re-open this issue?

Workaround #297 (comment) works in 1.12.3 for me while 1.12.4 is broken again.

The last thing I also upgraded was the Android Gradle Plugin - from 3.5.1 to 3.6.0-beta01

Reverting back to Android Gradle Plugin 3.5.1 makes mockk work again! Also works with Android Studio 3.6 Beta 2

So Android Gradle Plugin 3.6.0-beta01 caused this problem for me - stick to 3.5.1

Hope that helps

Weird: this issue was reported with MockK version: 1.9.3, but I don’t get this error with this version, but I do get it with 1.10.2

I have this same exact issue. I’m using mockk 1.9.3 until 1.10.x is fixed.

I’m seeing this issue on: mockk: 1.13.5 JDK: 9 Kotlin: 1.6.0 OS: 28+ JUnit 4.13.2

But it’s only happening for instrumented tests on android-library modules. The same config works fine for the app module.

We had a similar problem, namely androidTest failing with the following error:

java.lang.ExceptionInInitializerError
	at our.app.SuccessFragmentTest.<init>(SuccessFragmentTest.kt:54)
	at java.lang.reflect.Constructor.newInstance0(Native Method)
	at java.lang.reflect.Constructor.newInstance(Constructor.java:343)
	at org.junit.runners.BlockJUnit4ClassRunner.createTest(BlockJUnit4ClassRunner.java:217)
	at org.junit.runners.BlockJUnit4ClassRunner$1.runReflectiveCall(BlockJUnit4ClassRunner.java:266)
	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
	at org.junit.runners.BlockJUnit4ClassRunner.methodBlock(BlockJUnit4ClassRunner.java:263)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
	at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
	at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
	at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
	at org.junit.runners.Suite.runChild(Suite.java:128)
	at org.junit.runners.Suite.runChild(Suite.java:27)
	at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
	at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
	at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
	at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
	at org.junit.runner.JUnitCore.run(JUnitCore.java:115)
	at androidx.test.internal.runner.TestExecutor.execute(TestExecutor.java:56)
	at androidx.test.runner.AndroidJUnitRunner.onStart(AndroidJUnitRunner.java:395)
	at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:2205)
Caused by: io.mockk.proxy.MockKAgentException: MockK could not self-attach a jvmti agent to the current VM. This feature is required for inline mocking.
This error occured due to an I/O error during the creation of this agent: java.io.IOException: Native-bridge agents unsupported: libmockkjvmtiagent.so

Potentially, the current VM does not support the jvmti API correctly
	at io.mockk.proxy.android.AndroidMockKAgentFactory.init(AndroidMockKAgentFactory.kt:67)
	at io.mockk.impl.JvmMockKGateway.<init>(JvmMockKGateway.kt:46)
	at io.mockk.impl.JvmMockKGateway.<clinit>(JvmMockKGateway.kt:173)
	... 28 more
Caused by: java.io.IOException: Native-bridge agents unsupported: libmockkjvmtiagent.so
	at dalvik.system.VMDebug.nativeAttachAgent(Native Method)
	at dalvik.system.VMDebug.attachAgent(VMDebug.java:572)
	at android.os.Debug.attachJvmtiAgent(Debug.java:2563)
	at io.mockk.proxy.android.JvmtiAgent.<init>(JvmtiAgent.kt:48)
	at io.mockk.proxy.android.AndroidMockKAgentFactory.init(AndroidMockKAgentFactory.kt:40)
	... 30 more

This was solved by removing the following from our build.gradle.kts file:

android {
    defaultConfig {
        ndk {
            abiFilters("armeabi-v7a", "arm64-v8a")
        }
   }
}

This might not be an issue with this library, but when I googled our issue, this issue came up first. Hopefully this comment will help others in the future.

So I had two projects, one had this exact issue but the other one ran mockk just fine. I spent an hour trying to figure out what differs between the projects. Here is what I found.

In the project that mockk was working, I had the following dependencies from this library https://github.com/wseemann/FFmpegMediaMetadataRetriever

implementation 'com.github.wseemann:FFmpegMediaMetadataRetriever-core:1.0.15'
implementation 'com.github.wseemann:FFmpegMediaMetadataRetriever-native:1.0.15'

I added this to my other project, and also used it in a test ( val foo= FFmpegMediaMetadataRetriever()), now mockk works.

My versions: AGP: 7.0.0-alpha10 JDK: 13 Kotlin: 1.4.31 compile/targetSdk: 30, minSdk: 23

I have no idea why this would be the case but I guess because FFmpegMediaMetadataRetriever uses some native libraries. Hopefully this helps someone.

@treitter I cannot get it to work with only androidx.annotation:annotation:1.1.0

Including FFmegMediaMetadataRetriever-native worked around this bug for me as well. I looked at that project’s build.gradle files and narrowed it down so all I need to add to my build.gradle to avoid this bug is:

androidTestImplementation "androidx.annotation:annotation:1.1.0"

I’m not sure exactly why it works but I’m a lot more comfortable including this than FFmegMediaMetadataRetriever-native.

So, maybe MockK actually depends on androidx.annotation:annotation but the dependency doesn’t gets passed along correctly?

Weird: this issue was reported with MockK version: 1.9.3, but I don’t get this error with this version, but I do get it with 1.10.2

@jtrollkarl I’m running into this, too. And I think I have a root cause. The rest of the exception message complains about not being able to find libmockkjvmtiagent.so. As you can see in this screenshot (apkAnalyzer of my app’s test apk), the x86_64 and arm64-v8a binaries for libmockkjvmtiagent.so do not exist:

Screen Shot 2019-07-19 at 2 47 48 PM

I don’t seem to be having any issues on lower API levels.

Same here. That seems to be due to this SDK_INT check: https://github.com/mockk/mockk/blob/fdb6839b316bfee571a387852c3de7448a6eb64b/agent/android/src/main/kotlin/io/mockk/proxy/android/AndroidMockKAgentFactory.kt#L32

I don’t see anywhere else in mockk’s source that this particular exception can be thrown.

An additional contributing factor why you’re not seeing this on other API levels is probably that you’re loading a x86_64 or arm64-v8a in your tests on a device that is x86_64 or arm64-v8a hardware. Conveniently, x86_64 platform images only seem to be available from API 28 onwards on the google repository:

$ ./tools/bin/sdkmanager "system-images;android-27;google_apis;x86_64"
Warning: Failed to find package system-images;android-27;google_apis;x86_64     
Error: Package path is not valid. Valid system image paths are:ository...       
system-images;android-28;google_apis;x86
system-images;android-26;google_apis_playstore;x86
system-images;android-18;google_apis;x86
system-images;android-25;google_apis;x86
system-images;android-25;google_apis_playstore;x86
system-images;android-26;google_apis;x86
system-images;android-28;google_apis;x86_64
system-images;android-23;google_apis;x86
system-images;android-27;google_apis;x86
system-images;android-19;google_apis;x86
system-images;android-22;google_apis;x86
system-images;android-21;default;armeabi-v7a
system-images;android-21;google_apis;x86
system-images;android-22;default;x86
null

So you may see this if you test on emulators–since the platform emulated prior to API 28 are definitely 32-bit, they’d be loading the 32-bit versions of your shared objects and the 32-bit version of libmockkjvmtiagent.so, which exists.

So my suggestion to work around this for now is to exclude the 64-bit versions of the shared objects that you load from your test apks (which I realize defeats the point of testing them in the first place). You’d like to test the 64-bit versions to make it as close to production as possible, but it does not seem doable at the moment with mockk.

Likely, the motivation for including the 64-bit binaries is the Google Play requirement to include 64-bit native binaries by August 1st. That is the motivation for me. So it would be nice to see a 64-bit version of libmockkjvmtiagent.so.

@oleksiyp Do you think the x86_64 and arm64-v8a architectures will be supported soon?

So I then included FFmegMediaMetadataRetriever-native again and it didn’t work until I stashed my changes, switched branches back and forward in git. Whatever is happening it is extremely brittle.

I cannot confirm if androidx.annotation:annotation:1.1.0 actually works, can’t be bothered to test it now.

I have a simple workaround. Remove all of your instrumented tests from the app module. I put all my features and their tests in an Android library module. With that the mockk will work normally.

it was labeled by mistake