Data provider mismatch with @Mocked annotation #337

Open
AlexStasko opened this Issue Sep 21, 2016 · 7 comments

Projects

None yet

6 participants

@AlexStasko
AlexStasko commented Sep 21, 2016 edited

I use JMockit 1.27
Java 1.8.92
And Windows OS

public class DummyServiceTest {

     private DummyService dummyService = new DummyService();

     @Test(expectedExceptions = {RuntimeException.class})
     public void testStore(@Mocked DummyDomain unused)  {
         new StrictExpectations() {
             {
                 new DummyDomain("TEST1");

                 try {
                     unused.exec();
                 } catch (IOException e) {
                     // never happen
                }
                result = new IOException();
             }
         };

         dummyService.store("test");
     }
}

Report prints following error:

org.testng.internal.reflect.MethodMatcherException: 
Data provider mismatch
Method: testStore([Parameter{index=0, type=by.common.model.DummyDomain, declaredAnnotations=[@mockit.Mocked(stubOutClassInitialization=false)]}])
Arguments: [(java.lang.String)]
at org.testng.internal.reflect.DataProviderMethodMatcher.getConformingArguments(DataProviderMethodMatcher.java:52)
at org.testng.internal.Invoker.injectParameters(Invoker.java:1238)
at org.testng.internal.Invoker.invokeTestMethods(Invoker.java:1131)
at org.testng.internal.TestMethodWorker.invokeTestMethods(TestMethodWorker.java:129)
at org.testng.internal.TestMethodWorker.run(TestMethodWorker.java:112)
at org.testng.TestRunner.privateRun(TestRunner.java:749)
at org.testng.TestRunner.run(TestRunner.java:603)
at org.testng.SuiteRunner.runTest(SuiteRunner.java:368)
at org.testng.SuiteRunner.runSequentially(SuiteRunner.java:363)
at org.testng.SuiteRunner.privateRun(SuiteRunner.java:321)
at org.testng.SuiteRunner.run(SuiteRunner.java:270)
at org.testng.SuiteRunnerWorker.runSuite(SuiteRunnerWorker.java:52)
at org.testng.SuiteRunnerWorker.run(SuiteRunnerWorker.java:86)
at org.testng.TestNG.runSuitesSequentially(TestNG.java:1284)
at org.testng.TestNG.runSuitesLocally(TestNG.java:1209)
at org.testng.TestNG.runSuites(TestNG.java:1124)
at org.testng.TestNG.run(TestNG.java:1096)
at org.gradle.api.internal.tasks.testing.testng.TestNGTestClassProcessor.runTests(TestNGTestClassProcessor.java:130)
at org.gradle.api.internal.tasks.testing.testng.TestNGTestClassProcessor.stop(TestNGTestClassProcessor.java:80)
at org.gradle.api.internal.tasks.testing.SuiteTestClassProcessor.stop(SuiteTestClassProcessor.java:58)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.gradle.messaging.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:35)
at org.gradle.messaging.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
at org.gradle.messaging.dispatch.ContextClassLoaderDispatch.dispatch(ContextClassLoaderDispatch.java:32)
at org.gradle.messaging.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:93)
at com.sun.proxy.$Proxy2.stop(Unknown Source)
at org.gradle.api.internal.tasks.testing.worker.TestWorker.stop(TestWorker.java:116)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.gradle.messaging.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:35)
at org.gradle.messaging.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
at org.gradle.messaging.remote.internal.hub.MessageHub$Handler.run(MessageHub.java:360)
at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:54)
at org.gradle.internal.concurrent.StoppableExecutorImpl$1.run(StoppableExecutorImpl.java:40)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)

That problem happens with testng 6.9.11 and higher. With testng 6.9.10 this test runs successfully.
I am not sure the problem is in jmockit, but might be this is an issue somewhere in proxy that jmockit creates for method parameter.

Test project is here jmockit-check.zip

Thank you,
Alex

@AlexStasko AlexStasko changed the title from Coverage tool: Data provider mismatch with @Mocked annotation to Data provider mismatch with @Mocked annotation Sep 21, 2016
@juherr
juherr commented Sep 21, 2016

The problem is due to the new way to manage data provider parameters: cbeust/testng#923

Maybe JMockit should upgrade its TestNG support. But let me know if a specific spi is needed to help with the integration.

Ping @nitinverma who made the 6.9.11 modification, maybe he'll have a good idea for a nice integration.

For reference, we have cbeust/testng#600 in the backlog too which points some integration problems on dataprovider.

@rliesenfeld rliesenfeld self-assigned this Sep 21, 2016
@rliesenfeld
Member
rliesenfeld commented Sep 21, 2016 edited

TestNG should provide an extensible mechanism for third-party libraries to provide their own custom arguments to test methods, without them having to resort to hacks as I had to do in JMockit. JUnit 5 (not JUnit 4) does provide such a mechanism (the ParameterResolver extension interface), and it is used in JMockit.

I will see if the new parameter resolution in TestNG 6.9.11+can be supported. From the first look, it may be difficult. I don't exclude the possibility that mock parameters in test methods won't be usable anymore with these newer versions of TestNG; in that case, JMockit users would have to limit themselves to mock fields.

@nitinverma

Hi @AlexStasko, @rliesenfeld

testng is expecting an object that is an instance of 'DummyDomain' or null, as both are valid inputs to invoke this test method.

Following snippet would satisfy testng-6.9.11 for now.

   @DataProvider(name = "data")
    public Object[][] data() {
        return new Object[][]{
                new Object[]{null},
                new Object[]{new DummyDomain("from-data-provider")},
        };
    }

    @Test(dataProvider = "data", expectedExceptions = {RuntimeException.class})
    public void testStore(@Mocked DummyDomain unused) {
        System.out.println("unused:" + unused);
        new StrictExpectations() {
            {
                new DummyDomain("TEST1");

                try {
                    unused.exec();
                } catch (IOException e) {
                    // never happen
                }
                result = new IOException();
            }
        };

        dummyService.store("test");
    }

Happy to help n collaborate on this integration.

Regards,

Nitin Verma

@nitinverma

Refer https://github.com/jmockit/jmockit1
if getInjectedParameter(...) returns corresponding mock object in place of the empty string. Above test should start working. What additional context would you require to build a mock?

TestNGRunnerDecorator.java

   public static final class MockParameters extends MockUp<Parameters>
   {
      @Mock
      public static void checkParameterTypes(
         String methodName, Class<?>[] parameterTypes, String methodAnnotation, String[] parameterNames) {}

      @Mock
      @Nullable
      public static Object getInjectedParameter(
         @Nonnull Invocation invocation, Class<?> c, @Nullable Method method,
         ITestContext context, ITestResult testResult)
      {
         ((MockInvocation) invocation).prepareToProceedFromNonRecursiveMock();
         Object value = Parameters.getInjectedParameter(c, method, context, testResult);

         if (value != null) {
            return value;
         }

         if (method == null) {
            // Test execution didn't reach a test method yet.
            return null;
         }

         if (method.getParameterTypes().length == 0) {
            // A test method was reached, but it has no parameters.
            return null;
         }

         if (isMethodWithParametersProvidedByTestNG(method)) {
            // The test method has parameters, but they are to be provided by TestNG, not JMockit.
            return null;
         }

         // It's a mock parameter in a test method, to be provided by JMockit.
         return "";
      }
   }
@juherr
juherr commented Sep 22, 2016

mock parameters in test methods won't be usable anymore with these newer versions of TestNG

@rliesenfeld As a fix for this issue and even if it is in the internal package, you may use Version and provide a nice message to your users depending on the testng version they are using (instead of the exception).

@susau
susau commented Sep 23, 2016

If this can be fixed somehow before cbeust/testng#1164 is implemented it would be great. On a project we upgraded testng to 6.9.12 to fix a testng issue and faced that deal breaker issue.

Thanks

@Siarhei-Yarkavy

Faced this issue. @rliesenfeld could you please document the case in user guide and put an appropriate error message when newest TESTNG used if it is not possible to fix.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment