Skip to content

Commit 2394135

Browse files
committed
Improve error msg for non-static external @MethodSource factory methods
Prior to this commit, the error message was cryptic if an external factory method was non-static and the test class was configured to run with test instance per-class lifecycle semantics. Specifically, the error was the following from the native implementation of Method#invoke in the JDK. java.lang.IllegalArgumentException: object is not an instance of declaring class This commit builds on the work done in PR #3263 by expanding the "static is required" check to any resolved factory method that cannot be invoked on the current test instance (which may be null). Consequently, the isPerMethodLifecycle() check is no longer necessary. Closes #3276
1 parent d62feaa commit 2394135

File tree

2 files changed

+48
-28
lines changed

2 files changed

+48
-28
lines changed

junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/MethodArgumentsProvider.java

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@
2424

2525
import org.junit.jupiter.api.Test;
2626
import org.junit.jupiter.api.TestFactory;
27-
import org.junit.jupiter.api.TestInstance.Lifecycle;
2827
import org.junit.jupiter.api.TestTemplate;
2928
import org.junit.jupiter.api.extension.ExtensionContext;
3029
import org.junit.platform.commons.JUnitException;
@@ -51,7 +50,7 @@ protected Stream<? extends Arguments> provideArguments(ExtensionContext context,
5150
// @formatter:off
5251
return stream(methodNames)
5352
.map(factoryMethodName -> findFactoryMethod(testClass, testMethod, factoryMethodName))
54-
.map(factoryMethod -> validateFactoryMethod(context, factoryMethod))
53+
.map(factoryMethod -> validateFactoryMethod(context, factoryMethod, testInstance))
5554
.map(factoryMethod -> context.getExecutableInvoker().invoke(factoryMethod, testInstance))
5655
.flatMap(CollectionUtils::toStream)
5756
.map(MethodArgumentsProvider::toArguments);
@@ -176,8 +175,8 @@ private static Class<?> loadRequiredClass(String className) {
176175
cause -> new JUnitException(format("Could not load class [%s]", className), cause));
177176
}
178177

179-
private static Method validateFactoryMethod(ExtensionContext context, Method factoryMethod) {
180-
if (isPerMethodLifecycle(context)) {
178+
private static Method validateFactoryMethod(ExtensionContext context, Method factoryMethod, Object testInstance) {
179+
if (!factoryMethod.getDeclaringClass().isInstance(testInstance)) {
181180
Preconditions.condition(ReflectionUtils.isStatic(factoryMethod),
182181
() -> format("Method '%s' must be static: local factory methods must be static unless "
183182
+ "the test class is annotated with @TestInstance(Lifecycle.PER_CLASS); external "
@@ -187,10 +186,6 @@ private static Method validateFactoryMethod(ExtensionContext context, Method fac
187186
return factoryMethod;
188187
}
189188

190-
private static boolean isPerMethodLifecycle(ExtensionContext context) {
191-
return context.getTestInstanceLifecycle().orElse(Lifecycle.PER_CLASS) == Lifecycle.PER_METHOD;
192-
}
193-
194189
private static Arguments toArguments(Object item) {
195190

196191
// Nothing to do except cast.

junit-jupiter-params/src/test/java/org/junit/jupiter/params/provider/MethodArgumentsProviderTests.java

Lines changed: 45 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -174,10 +174,38 @@ void providesArgumentsUsingListOfObjectArrays() {
174174
}
175175

176176
@Test
177-
void throwsExceptionWhenNonStaticFactoryMethodIsReferencedAndStaticIsRequired() {
177+
void throwsExceptionWhenNonStaticLocalFactoryMethodIsReferencedWithLifecyclePerMethodSemantics() {
178+
var lifecyclePerClass = false;
178179
var exception = assertThrows(PreconditionViolationException.class,
179-
() -> provideArguments(NonStaticTestCase.class, null, false, "nonStaticStringStreamProvider").toArray());
180+
() -> provideArguments(NonStaticTestCase.class, lifecyclePerClass,
181+
"nonStaticStringStreamProvider").toArray());
180182

183+
assertStaticIsRequired(exception);
184+
}
185+
186+
@Test
187+
void throwsExceptionWhenNonStaticExternalFactoryMethodIsReferencedWithLifecyclePerMethodSemantics() {
188+
var factoryClass = NonStaticTestCase.class.getName();
189+
var factoryMethod = factoryClass + "#nonStaticStringStreamProvider";
190+
var lifecyclePerClass = false;
191+
var exception = assertThrows(PreconditionViolationException.class,
192+
() -> provideArguments(TestCase.class, lifecyclePerClass, factoryMethod).toArray());
193+
194+
assertStaticIsRequired(exception);
195+
}
196+
197+
@Test
198+
void throwsExceptionWhenNonStaticExternalFactoryMethodIsReferencedWithLifecyclePerClassSemantics() {
199+
var factoryClass = NonStaticTestCase.class.getName();
200+
var factoryMethod = factoryClass + "#nonStaticStringStreamProvider";
201+
boolean lifecyclePerClass = true;
202+
var exception = assertThrows(PreconditionViolationException.class,
203+
() -> provideArguments(TestCase.class, lifecyclePerClass, factoryMethod).toArray());
204+
205+
assertStaticIsRequired(exception);
206+
}
207+
208+
private static void assertStaticIsRequired(PreconditionViolationException exception) {
181209
assertThat(exception).hasMessageContainingAll("Method '",
182210
"' must be static: local factory methods must be static ",
183211
"unless the test class is annotated with @TestInstance(Lifecycle.PER_CLASS); ",
@@ -186,7 +214,7 @@ void throwsExceptionWhenNonStaticFactoryMethodIsReferencedAndStaticIsRequired()
186214

187215
@Test
188216
void providesArgumentsFromNonStaticFactoryMethodWhenStaticIsNotRequired() {
189-
var arguments = provideArguments(NonStaticTestCase.class, null, true, "nonStaticStringStreamProvider");
217+
var arguments = provideArguments(NonStaticTestCase.class, true, "nonStaticStringStreamProvider");
190218

191219
assertThat(arguments).containsExactly(array("foo"), array("bar"));
192220
}
@@ -609,7 +637,7 @@ void throwsExceptionWhenExternalFactoryMethodHasInvalidReturnType() {
609637
String factoryMethod = factoryClass + "#factoryWithInvalidReturnType";
610638

611639
var exception = assertThrows(PreconditionViolationException.class,
612-
() -> provideArguments(TestCase.class, null, false, factoryMethod).toArray());
640+
() -> provideArguments(TestCase.class, false, factoryMethod).toArray());
613641

614642
assertThat(exception.getMessage())//
615643
.containsSubsequence("Could not find valid factory method [" + factoryMethod + "] for test class [",
@@ -676,30 +704,24 @@ private static Object[] array(Object... objects) {
676704
}
677705

678706
private Stream<Object[]> provideArguments(String... factoryMethodNames) {
679-
return provideArguments(TestCase.class, null, false, factoryMethodNames);
707+
return provideArguments(TestCase.class, false, factoryMethodNames);
680708
}
681709

682710
private Stream<Object[]> provideArguments(Method testMethod, String factoryMethodName) {
683711
return provideArguments(TestCase.class, testMethod, false, factoryMethodName);
684712
}
685713

686-
private Stream<Object[]> provideArguments(Class<?> testClass, Method testMethod, boolean allowNonStaticMethod,
714+
private Stream<Object[]> provideArguments(Class<?> testClass, boolean allowNonStaticMethod,
687715
String... factoryMethodNames) {
688716

689-
if (testMethod == null) {
690-
try {
691-
class DummyClass {
692-
@SuppressWarnings("unused")
693-
public void dummyMethod() {
694-
};
695-
}
696-
// ensure we have a non-null method, even if it's not a real test method.
697-
testMethod = DummyClass.class.getMethod("dummyMethod");
698-
}
699-
catch (Exception ex) {
700-
throw new RuntimeException(ex);
701-
}
702-
}
717+
// Ensure we have a non-null test method, even if it's not a real test method.
718+
// If this throws an exception, make sure that the supplied test class defines a "void test()" method.
719+
Method testMethod = ReflectionUtils.findMethod(testClass, "test").get();
720+
return provideArguments(testClass, testMethod, allowNonStaticMethod, factoryMethodNames);
721+
}
722+
723+
private Stream<Object[]> provideArguments(Class<?> testClass, Method testMethod, boolean allowNonStaticMethod,
724+
String... factoryMethodNames) {
703725

704726
var methodSource = mock(MethodSource.class);
705727

@@ -940,6 +962,9 @@ static Object[][] twoDimensionalObjectArrayProvider() {
940962
// This test case mimics @TestInstance(Lifecycle.PER_CLASS)
941963
static class NonStaticTestCase {
942964

965+
void test() {
966+
}
967+
943968
Stream<String> nonStaticStringStreamProvider() {
944969
return Stream.of("foo", "bar");
945970
}

0 commit comments

Comments
 (0)