Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Mocking with explicit constructor fails with varargs parameter #2601

Closed
jeff303 opened this issue Mar 18, 2022 · 13 comments
Closed

Mocking with explicit constructor fails with varargs parameter #2601

jeff303 opened this issue Mar 18, 2022 · 13 comments

Comments

@jeff303
Copy link

jeff303 commented Mar 18, 2022

Consider the following test

import org.junit.Test;
import org.mockito.Mockito;

public class MockitoTest {

    private static class A {
        A(String a, int b, AutoCloseable... c) {
            System.out.println("Constructor called");
        }
    }

    @Test
    public void testIssue() {

        AutoCloseable c = () -> {
            System.out.println("Closing");
        };
        A a = Mockito.mock(A.class, Mockito.withSettings().useConstructor("hi", 1, new AutoCloseable[] {c}));

    }
}

On Temurin JDK 11, OS X, mockito-core 4.4.0, this fails with:

Unable to create mock instance of type 'A'
org.mockito.exceptions.base.MockitoException: Unable to create mock instance of type 'A'
	at MockitoTest.testIssue(MockitoTest.java:20)
	at java.base@11.0.14/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base@11.0.14/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at java.base@11.0.14/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base@11.0.14/java.lang.reflect.Method.invoke(Method.java:566)
	at app//org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:59)
	at app//org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
	at app//org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:56)
	at app//org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
	at app//org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
	at app//org.junit.runners.BlockJUnit4ClassRunner$1.evaluate(BlockJUnit4ClassRunner.java:100)
	at app//org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366)
	at app//org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:103)
	at app//org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:63)
	at app//org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)
	at app//org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)
	at app//org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
	at app//org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)
	at app//org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)
	at app//org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
	at app//org.junit.runners.ParentRunner.run(ParentRunner.java:413)
	at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.runTestClass(JUnitTestClassExecutor.java:110)
	at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.execute(JUnitTestClassExecutor.java:58)
	at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.execute(JUnitTestClassExecutor.java:38)
	at org.gradle.api.internal.tasks.testing.junit.AbstractJUnitTestClassProcessor.processTestClass(AbstractJUnitTestClassProcessor.java:62)
	at org.gradle.api.internal.tasks.testing.SuiteTestClassProcessor.processTestClass(SuiteTestClassProcessor.java:51)
	at java.base@11.0.14/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base@11.0.14/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at java.base@11.0.14/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base@11.0.14/java.lang.reflect.Method.invoke(Method.java:566)
	at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:36)
	at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
	at org.gradle.internal.dispatch.ContextClassLoaderDispatch.dispatch(ContextClassLoaderDispatch.java:33)
	at org.gradle.internal.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:94)
	at com.sun.proxy.$Proxy2.processTestClass(Unknown Source)
	at org.gradle.api.internal.tasks.testing.worker.TestWorker$2.run(TestWorker.java:176)
	at org.gradle.api.internal.tasks.testing.worker.TestWorker.executeAndMaintainThreadName(TestWorker.java:129)
	at org.gradle.api.internal.tasks.testing.worker.TestWorker.execute(TestWorker.java:100)
	at org.gradle.api.internal.tasks.testing.worker.TestWorker.execute(TestWorker.java:60)
	at org.gradle.process.internal.worker.child.ActionExecutionWorker.execute(ActionExecutionWorker.java:56)
	at org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker.call(SystemApplicationClassLoaderWorker.java:133)
	at org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker.call(SystemApplicationClassLoaderWorker.java:71)
	at app//worker.org.gradle.process.internal.worker.GradleWorkerMain.run(GradleWorkerMain.java:69)
	at app//worker.org.gradle.process.internal.worker.GradleWorkerMain.main(GradleWorkerMain.java:74)
Caused by: org.mockito.creation.instance.InstantiationException: 
Unable to create instance of 'A'.
Please ensure the target class has a constructor that matches these argument types: [java.lang.String, java.lang.Integer, [Ljava.lang.AutoCloseable;] and executes cleanly.
	... 44 more
Caused by: java.lang.reflect.InvocationTargetException
	at org.mockito.internal.util.reflection.InstrumentationMemberAccessor.newInstance(InstrumentationMemberAccessor.java:198)
	at org.mockito.internal.util.reflection.InstrumentationMemberAccessor.newInstance(InstrumentationMemberAccessor.java:161)
	at org.mockito.internal.util.reflection.ModuleMemberAccessor.newInstance(ModuleMemberAccessor.java:42)
	at org.mockito.internal.creation.instance.ConstructorInstantiator.invokeConstructor(ConstructorInstantiator.java:70)
	at org.mockito.internal.creation.instance.ConstructorInstantiator.withParams(ConstructorInstantiator.java:53)
	at org.mockito.internal.creation.instance.ConstructorInstantiator.newInstance(ConstructorInstantiator.java:39)
	at org.mockito.internal.creation.bytebuddy.InlineDelegateByteBuddyMockMaker.doCreateMock(InlineDelegateByteBuddyMockMaker.java:358)
	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)
	... 44 more
Caused by: java.lang.ClassCastException: Cannot cast [Ljava.lang.AutoCloseable; to java.lang.AutoCloseable
	at java.base/java.lang.Class.cast(Class.java:3605)
	at java.base/java.lang.invoke.MethodHandle.invokeWithArguments(MethodHandle.java:710)
	at java.base/java.lang.invoke.MethodHandleImpl$AsVarargsCollector.invokeWithArguments(MethodHandleImpl.java:578)
	at org.mockito.internal.util.reflection.InstrumentationMemberAccessor$Dispatcher$ByteBuddy$2DHh04hv.invokeWithArguments(Unknown Source)
	at org.mockito.internal.util.reflection.InstrumentationMemberAccessor.lambda$newInstance$0(InstrumentationMemberAccessor.java:191)
	at org.mockito.internal.util.reflection.InstrumentationMemberAccessor.newInstance(InstrumentationMemberAccessor.java:188)
	... 55 more

Changing the constructor param c to be AutoCloseable[] makes it work. Is it possible to invoke the constructor with varargs in this case?

@TimvdLippe
Copy link
Contributor

I think the correct usage should be Mockito.withSettings().useConstructor("hi", 1, c), would that work? In case you want to specify multiple, it would become Mockito.withSettings().useConstructor("hi", 1, c1, c2).

@jeff303
Copy link
Author

jeff303 commented Mar 21, 2022

I think the correct usage should be Mockito.withSettings().useConstructor("hi", 1, c), would that work? In case you want to specify multiple, it would become Mockito.withSettings().useConstructor("hi", 1, c1, c2).

This: Mockito.mock(A.class, Mockito.withSettings().useConstructor("hi", 1, c))

fails with:

Caused by: org.mockito.creation.instance.InstantiationException: 
Unable to create instance of 'A'.
Please ensure that the target class has a constructor that matches these argument types: [java.lang.String, java.lang.Integer, MockitoTest$$Lambda$47/0x0000000800154840].
	... 44 more

I also tried with additional varargs values, and the exception thrown is similar.

@alvaro-martinez-hernandez
Copy link

alvaro-martinez-hernandez commented Jul 15, 2022

@jeff303 The actual problem with your test is that the A class should be public, and its constructor as well. Otherwise Mockito can't find the constructor.

@TimvdLippe On the other hand, there is a "problem" with the withSettings().useConstructor() method: Mockito is unable to accept varargs without an array. Check this out:

@RunWith(MockitoJUnitRunner.class)
public class FakeClassTest extends TestBase {

    public static class A {

        public A(String a, int b, AutoCloseable... c) {
            System.out.println("Constructor called");
        }
    }

    @Test
    public void withArray() {

        AutoCloseable c = () -> {
            System.out.println("Closing");
        };
        A a = Mockito.mock(A.class, Mockito.withSettings().useConstructor("hi", 1, new AutoCloseable[]{c}));

    }

    @Test
    public void withoutArray() {

        AutoCloseable c = () -> {
            System.out.println("Closing");
        };
        A a = Mockito.mock(A.class, Mockito.withSettings().useConstructor("hi", 1, c));

    }
}

@big-andy-coates
Copy link
Contributor

big-andy-coates commented Nov 30, 2022

FYI @TimvdLippe, the test case in the description of this issue runs just find on the latest code (4.9.0).

Looks like this issue can be closed.

@TimvdLippe
Copy link
Contributor

Thanks for confirming!

@yashmayya
Copy link

This issue occurs even with Mockito 4.11.0 (the latest 4.x version currently). I tried reproducing it with JDK 8, 11, 17 - the issue occurs on JDK 11 and JDK 17 but not on JDK 8. @TimvdLippe can this be re-opened?

@TimvdLippe
Copy link
Contributor

@yashmayya Can you please open a PR to Mockito with the regression test so that we can confirm it still breaks and work towards fixing it?

@yashmayya
Copy link

@TimvdLippe here's the PR with the regression test - #3081. Looks like it's a fairly rarely encountered issue because it only occurs when using the inline mock maker with the member accessor module on Java 9 and newer.

@TimvdLippe TimvdLippe reopened this Aug 8, 2023
@big-andy-coates
Copy link
Contributor

Happy to take a look if we can merge in the test.

@yashmayya
Copy link

Is there a better way to configure the member accessor plugin? The draft PR I pushed out to demonstrate the bug will cause every test to use member-accessor-module which is probably not what we want.

@TimvdLippe
Copy link
Contributor

We have a GitHub job matrix which should already be running at least that configuration. So maybe that file is not necessary and removing it will correctly fail only the job with the accessor.

@yashmayya
Copy link

Thanks Tim, that makes sense. I've removed that file.

@TimvdLippe
Copy link
Contributor

Closing this per #3081 (comment)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants