Repository: mockito/mockito
Issue: javax.tools.ToolProvider could not be found in InlineByteBuddyMockMaker
Labels: please contribute

Hi guys

I'm encountering a crash when I run `AndroidTest` that contain mocking class cause the ByteBuddy didn't found `javax.tools.ToolProvider`. I tried to reinstall JDK 8 but doesn't work. It seems to be `javax.tools.ToolProvider` not existing in it. 

Could you guys help me please?

Cheer,

Natthawut

## Environment
------------------------------------------------------------
Gradle 5.4.1
------------------------------------------------------------

Build time:   2019-04-26 08:14:42 UTC
Revision:     261d171646b36a6a28d5a19a69676cd098a4c19d

Kotlin:       1.3.21
Groovy:       2.5.4
Ant:          Apache Ant(TM) version 1.9.13 compiled on July 10 2018
JVM:          1.8.0_231 (Oracle Corporation 25.231-b11)
OS:           Mac OS X 10.15.2 x86_64

## Dependencies

```
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation"org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
    implementation 'androidx.appcompat:appcompat:1.1.0'
    implementation 'androidx.core:core-ktx:1.1.0'
    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
    testImplementation 'junit:junit:4.12'
    testImplementation 'org.mockito:mockito-core:3.2.4'
    androidTestImplementation 'androidx.test.ext:junit:1.1.1'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
    androidTestImplementation 'org.mockito:mockito-android:3.2.4'
    androidTestImplementation "io.mockk:mockk-android:1.9.3.kotlin12"
```

## Strack trace

```
E/TestRunner: failed: mockFoo(co.sample.mockitoandroid.MockWithMockitoTest)
    ----- begin exception -----
E/TestRunner: java.lang.IllegalStateException: Could not initialize plugin: interface org.mockito.plugins.MockMaker (alternate: null)
        at org.mockito.internal.configuration.plugins.PluginLoader$1.invoke(PluginLoader.java:74)
        at java.lang.reflect.Proxy.invoke(Proxy.java:1006)
        at $Proxy3.isTypeMockable(Unknown Source)
        at org.mockito.internal.util.MockUtil.typeMockabilityOf(MockUtil.java:29)
        at org.mockito.internal.util.MockCreationValidator.validateType(MockCreationValidator.java:22)
        at org.mockito.internal.creation.MockSettingsImpl.validatedSettings(MockSettingsImpl.java:241)
        at org.mockito.internal.creation.MockSettingsImpl.build(MockSettingsImpl.java:229)
        at org.mockito.internal.MockitoCore.mock(MockitoCore.java:62)
        at org.mockito.Mockito.mock(Mockito.java:1908)
        at org.mockito.Mockito.mock(Mockito.java:1817)
        at co.sample.mockitoandroid.MockWithMockitoTest.<init>(MockWithMockitoTest.kt:17)
        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 androidx.test.ext.junit.runners.AndroidJUnit4.run(AndroidJUnit4.java:104)
        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:392)
        at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:2145)
     Caused by: java.lang.NoClassDefFoundError: Failed resolution of: Ljavax/tools/ToolProvider;
        at org.mockito.internal.creation.bytebuddy.InlineByteBuddyMockMaker.<init>(InlineByteBuddyMockMaker.java:172)
        at java.lang.Class.newInstance(Native Method)
        at org.mockito.internal.configuration.plugins.PluginInitializer.loadImpl(PluginInitializer.java:49)
        at org.mockito.internal.configuration.plugins.PluginLoader.loadPlugin(PluginLoader.java:57)
        at org.mockito.internal.configuration.plugins.PluginLoader.loadPlugin(PluginLoader.java:44)
        at org.mockito.internal.configuration.plugins.PluginRegistry.<init>(PluginRegistry.java:22)
        at org.mockito.internal.configuration.plugins.Plugins.<clinit>(Plugins.java:19)
        at org.mockito.internal.configuration.plugins.Plugins.getMockMaker(Plugins.java:35)
        at org.mockito.internal.util.MockUtil.<clinit>(MockUtil.java:24)
        at org.mockito.internal.util.MockUtil.typeMockabilityOf(MockUtil.java:29) 
        at org.mockito.internal.util.MockCreationValidator.validateType(MockCreationValidator.java:22) 
        at org.mockito.internal.creation.MockSettingsImpl.validatedSettings(MockSettingsImpl.java:241) 
        at org.mockito.internal.creation.MockSettingsImpl.build(MockSettingsImpl.java:229) 
        at org.mockito.internal.MockitoCore.mock(MockitoCore.java:62) 
        at org.mockito.Mockito.mock(Mockito.java:1908) 
        at org.mockito.Mockito.mock(Mockito.java:1817) 
        at co.sample.mockitoandroid.MockWithMockitoTest.<init>(MockWithMockitoTest.kt:17) 
        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 androidx.test.ext.junit.runners.AndroidJUnit4.run(AndroidJUnit4.java:104) 
        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:392) 
        at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:2145) 
     Caused by: java.lang.ClassNotFoundException: Didn't find class "javax.tools.ToolProvider" on path: DexPathList[[zip file "/system/framework/android.test.runner.jar", zip file "/system/framework/android.test.mock.jar", zip file "/data/app/co.sample.mockitoandroid.test-ZJFawUg-
    ----- end exception -----
```
<img width="968" alt="Screen Shot 2563-01-07 at 12 45 26" src="https://user-images.githubusercontent.com/2638321/71871412-a93ee100-314b-11ea-9815-31a5d95056d4.png">


[MockitoAndroid.zip](https://github.com/mockito/mockito/files/4029049/MockitoAndroid.zip)







Issue comments:
I think this commit is part of the problem: https://github.com/mockito/mockito/pull/1567/files
> I think this commit is part of the problem: https://github.com/mockito/mockito/pull/1567/files

I tried with mockito 2.22.0, which should be a release before  the commit, but the error is still there. https://github.com/mockito/mockito/releases?after=v2.23.0

edit: what fixed the issue for me was removing the `mock-maker-inline` resource file
The inline mockmaker is not supported on Android (as listed in the exception). However, we should probably not crash before we can show that message to the user. I would welcome a PR to fix this problem.
@TimvdLippe What should people do for android then? Kotlin uses final classes only.
@raphw What is our advice for Android? I don't remember what our final resolution was for inline + android for Kotlin.
I just started using https://mockk.io/. Its Kotlin friendly also
On Android, the inline mock maker won't work (neither will Mockk which uses the same approach as we do). It does not support the instrumentation API alltogether.

I do agree that the error message is misleading. Currently preparing a PR to improve this.
You can achieve this by using com.linkedin.dexmaker:dexmaker-mockito-inline for APIs >= 28

Pull request review comments:

Patch that resolved the issue:
diff --git a/src/main/java/org/mockito/internal/configuration/InjectingAnnotationEngine.java b/src/main/java/org/mockito/internal/configuration/InjectingAnnotationEngine.java
index 2f6667015c..59239a4674 100644
--- a/src/main/java/org/mockito/internal/configuration/InjectingAnnotationEngine.java
+++ b/src/main/java/org/mockito/internal/configuration/InjectingAnnotationEngine.java
@@ -58,7 +58,7 @@ private List<AutoCloseable> processInjectMocks(
         List<AutoCloseable> closeables = new ArrayList<>();
         Class<?> classContext = clazz;
         while (classContext != Object.class) {
-            closeables.add(injectMocks(testInstance));
+            closeables.add(injectCloseableMocks(testInstance));
             classContext = classContext.getSuperclass();
         }
         return closeables;
@@ -79,6 +79,20 @@ private List<AutoCloseable> processIndependentAnnotations(
         return closeables;
     }
 
+    /**
+     * Required by PowerMockito and retained to avoid API breakage despite being internal API.
+     *
+     * @deprecated Use {@link InjectingAnnotationEngine#injectCloseableMocks(Object)}.
+     */
+    @Deprecated
+    public void injectMocks(Object testClassInstance) {
+        try {
+            injectCloseableMocks(testClassInstance).close();
+        } catch (Exception e) {
+            throw new IllegalStateException(e);
+        }
+    }
+
     /**
      * Initializes mock/spies dependencies for objects annotated with
      * &#064;InjectMocks for given testClassInstance.
@@ -88,7 +102,7 @@ private List<AutoCloseable> processIndependentAnnotations(
      * @param testClassInstance
      *            Test class, usually <code>this</code>
      */
-    private AutoCloseable injectMocks(final Object testClassInstance) {
+    private AutoCloseable injectCloseableMocks(final Object testClassInstance) {
         Class<?> clazz = testClassInstance.getClass();
         Set<Field> mockDependentFields = new HashSet<Field>();
         Set<Object> mocks = newMockSafeHashSet();
diff --git a/src/main/java/org/mockito/internal/creation/bytebuddy/InlineByteBuddyMockMaker.java b/src/main/java/org/mockito/internal/creation/bytebuddy/InlineByteBuddyMockMaker.java
index 89c1ac8622..097b8c8aae 100644
--- a/src/main/java/org/mockito/internal/creation/bytebuddy/InlineByteBuddyMockMaker.java
+++ b/src/main/java/org/mockito/internal/creation/bytebuddy/InlineByteBuddyMockMaker.java
@@ -4,15 +4,13 @@
  */
 package org.mockito.internal.creation.bytebuddy;
 
-import static org.mockito.internal.creation.bytebuddy.InlineBytecodeGenerator.EXCLUDES;
-import static org.mockito.internal.util.StringUtil.join;
-
 import java.io.File;
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.lang.instrument.Instrumentation;
 import java.lang.reflect.Modifier;
+import java.util.Arrays;
 import java.util.Map;
 import java.util.WeakHashMap;
 import java.util.concurrent.ConcurrentHashMap;
@@ -20,8 +18,6 @@
 import java.util.jar.JarFile;
 import java.util.jar.JarOutputStream;
 
-import javax.tools.ToolProvider;
-
 import net.bytebuddy.agent.ByteBuddyAgent;
 import org.mockito.Incubating;
 import org.mockito.creation.instance.Instantiator;
@@ -35,6 +31,9 @@
 import org.mockito.mock.MockCreationSettings;
 import org.mockito.plugins.InlineMockMaker;
 
+import static org.mockito.internal.creation.bytebuddy.InlineBytecodeGenerator.*;
+import static org.mockito.internal.util.StringUtil.*;
+
 /**
  * Agent and subclass based mock maker.
  * <p>
@@ -191,12 +190,34 @@ public class InlineByteBuddyMockMaker implements ClassCreatingMockMaker, InlineM
 
     public InlineByteBuddyMockMaker() {
         if (INITIALIZATION_ERROR != null) {
+            String detail;
+            if (System.getProperty("java.specification.vendor", "")
+                    .toLowerCase()
+                    .contains("android")) {
+                detail =
+                        "It appears as if you are trying to run this mock maker on Android which does not support the instrumentation API.";
+            } else {
+                try {
+                    if (Class.forName("javax.tools.ToolProvider")
+                                    .getMethod("getSystemJavaCompiler")
+                                    .invoke(null)
+                            == null) {
+                        detail =
+                                "It appears as if you are running on a JRE. Either install a JDK or add JNA to the class path.";
+                    } else {
+                        detail =
+                                "It appears as if your JDK does not supply a working agent attachment mechanism.";
+                    }
+                } catch (Throwable ignored) {
+                    detail =
+                            "It appears as if you are running an incomplete JVM installation that might not support all tooling APIs";
+                }
+            }
             throw new MockitoInitializationException(
                     join(
-                            "Could not initialize inline Byte Buddy mock maker. (This mock maker is not supported on Android.)",
-                            ToolProvider.getSystemJavaCompiler() == null
-                                    ? "Are you running a JRE instead of a JDK? The inline mock maker needs to be run on a JDK.\n"
-                                    : "",
+                            "Could not initialize inline Byte Buddy mock maker.",
+                            "",
+                            detail,
                             Platform.describe()),
                     INITIALIZATION_ERROR);
         }
@@ -384,11 +405,12 @@ public <T> StaticMockControl<T> createStaticMock(
                             + "to avoid infinitive loops within Mockito's implementation of static mock handling");
         } else if (type == Thread.class
                 || type == System.class
+                || type == Arrays.class
                 || ClassLoader.class.isAssignableFrom(type)) {
             throw new MockitoException(
                     "It is not possible to mock static methods of "
                             + type.getTypeName()
-                            + "to avoid interfering with the class loading mechanism");
+                            + " to avoid interfering with class loading what leads to infinite loops");
         }
 
         bytecodeGenerator.mockClassStatic(type);


Test patch: