diff options
author | Jens Nyman <jnyman@google.com> | 2023-10-24 14:24:34 +0000 |
---|---|---|
committer | Jens Nyman <jnyman@google.com> | 2023-10-24 14:24:34 +0000 |
commit | 39f38f236b3472cfc993565022ef17e891b6a198 (patch) | |
tree | d003f6b671829e65037775469236270332772e42 | |
parent | 26744e06e2a05f6967fc4273b7eeada58347f7f9 (diff) | |
download | TestParameterInjector-39f38f236b3472cfc993565022ef17e891b6a198.tar.gz |
Add support for Powermock by making getOnlyConstructor() more lenient (filter out non-public constructors).
This is consistent with how JUnit's getOnlyConstructor() is implemented.
Fixes https://github.com/google/TestParameterInjector/issues/40
9 files changed, 136 insertions, 53 deletions
diff --git a/junit4/src/main/java/com/google/testing/junit/testparameterinjector/PluggableTestRunner.java b/junit4/src/main/java/com/google/testing/junit/testparameterinjector/PluggableTestRunner.java index 5f4c061..b2a0ad8 100644 --- a/junit4/src/main/java/com/google/testing/junit/testparameterinjector/PluggableTestRunner.java +++ b/junit4/src/main/java/com/google/testing/junit/testparameterinjector/PluggableTestRunner.java @@ -316,7 +316,8 @@ abstract class PluggableTestRunner extends BlockJUnit4ClassRunner { private Object createTestForMethod(FrameworkMethod method) throws Exception { TestInfo testInfo = ((OverriddenFrameworkMethod) method).getTestInfo(); - Constructor<?> constructor = getTestClass().getOnlyConstructor(); + Constructor<?> constructor = + TestParameterInjectorUtils.getOnlyConstructor(getTestClass().getJavaClass()); // Construct a test instance Object testInstance; @@ -343,7 +344,9 @@ abstract class PluggableTestRunner extends BlockJUnit4ClassRunner { @Override protected final void validateZeroArgConstructor(List<Throwable> errorsReturned) { ExecutableValidationResult validationResult = - getTestMethodProcessors().validateConstructor(getTestClass().getOnlyConstructor()); + getTestMethodProcessors() + .validateConstructor( + TestParameterInjectorUtils.getOnlyConstructor(getTestClass().getJavaClass())); if (validationResult.wasValidated()) { errorsReturned.addAll(validationResult.validationErrors()); diff --git a/junit4/src/main/java/com/google/testing/junit/testparameterinjector/TestParameterAnnotationMethodProcessor.java b/junit4/src/main/java/com/google/testing/junit/testparameterinjector/TestParameterAnnotationMethodProcessor.java index 31ecf25..4132f12 100644 --- a/junit4/src/main/java/com/google/testing/junit/testparameterinjector/TestParameterAnnotationMethodProcessor.java +++ b/junit4/src/main/java/com/google/testing/junit/testparameterinjector/TestParameterAnnotationMethodProcessor.java @@ -867,7 +867,8 @@ final class TestParameterAnnotationMethodProcessor implements TestMethodProcesso case FIELD: // Fall through. case CLASS: return getAnnotationListWithType( - getOnlyConstructor(testClass).getAnnotations(), + TestParameterInjectorUtils.getOnlyConstructor(testClass) + .getAnnotations(), annotationTypeOrigin.annotationType()) .isEmpty(); default: @@ -890,7 +891,7 @@ final class TestParameterAnnotationMethodProcessor implements TestMethodProcesso Origin origin = annotationTypeOrigin.origin(); Class<? extends Annotation> annotationType = annotationTypeOrigin.annotationType(); if (origin == Origin.CONSTRUCTOR_PARAMETER) { - Constructor<?> constructor = getOnlyConstructor(testClass); + Constructor<?> constructor = TestParameterInjectorUtils.getOnlyConstructor(testClass); List<AnnotationWithMetadata> annotations = getAnnotationWithMetadataListWithType(constructor, annotationType); @@ -898,7 +899,8 @@ final class TestParameterAnnotationMethodProcessor implements TestMethodProcesso return toTestParameterValueList(annotations, origin); } } else if (origin == Origin.CONSTRUCTOR) { - Annotation annotation = getOnlyConstructor(testClass).getAnnotation(annotationType); + Annotation annotation = + TestParameterInjectorUtils.getOnlyConstructor(testClass).getAnnotation(annotationType); if (annotation != null) { return ImmutableList.of( TestParameterValueHolder.create( @@ -1022,15 +1024,6 @@ final class TestParameterAnnotationMethodProcessor implements TestMethodProcesso .toList(); } - private static Constructor<?> getOnlyConstructor(Class<?> testClass) { - Constructor<?>[] constructors = testClass.getDeclaredConstructors(); - checkState( - constructors.length == 1, - "a single public constructor is required for class %s", - testClass); - return constructors[0]; - } - @Override public void postProcessTestInstance(Object testInstance, TestInfo testInfo) { TestIndexHolder testIndexHolder = testInfo.getAnnotation(TestIndexHolder.class); diff --git a/junit4/src/main/java/com/google/testing/junit/testparameterinjector/TestParameterInjectorUtils.java b/junit4/src/main/java/com/google/testing/junit/testparameterinjector/TestParameterInjectorUtils.java new file mode 100644 index 0000000..215719a --- /dev/null +++ b/junit4/src/main/java/com/google/testing/junit/testparameterinjector/TestParameterInjectorUtils.java @@ -0,0 +1,47 @@ +/* + * Copyright 2023 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package com.google.testing.junit.testparameterinjector; + +import static com.google.common.base.Preconditions.checkState; +import static com.google.common.collect.Iterables.getOnlyElement; + +import com.google.common.collect.ImmutableList; +import java.lang.reflect.Constructor; + +/** Shared utility methods. */ +class TestParameterInjectorUtils { + + /** + * Return the only public constructor of the given test class. If there is none, return the only + * constructor. + * + * <p>Normally, there should be exactly one constructor (public or other), but some frameworks + * introduce an extra non-public constructor (see + * https://github.com/google/TestParameterInjector/issues/40). + */ + static Constructor<?> getOnlyConstructor(Class<?> testClass) { + ImmutableList<Constructor<?>> constructors = ImmutableList.copyOf(testClass.getConstructors()); + if (constructors.isEmpty()) { + // There are no public constructors. This is likely a JUnit5 test, so we should take the only + // non-public constructor instead. + constructors = ImmutableList.copyOf(testClass.getDeclaredConstructors()); + } + checkState( + constructors.size() == 1, "Expected exactly one constructor, but got %s", constructors); + return getOnlyElement(constructors); + } + + private TestParameterInjectorUtils() {} +} diff --git a/junit4/src/main/java/com/google/testing/junit/testparameterinjector/TestParametersMethodProcessor.java b/junit4/src/main/java/com/google/testing/junit/testparameterinjector/TestParametersMethodProcessor.java index 1a8d022..7dffc29 100644 --- a/junit4/src/main/java/com/google/testing/junit/testparameterinjector/TestParametersMethodProcessor.java +++ b/junit4/src/main/java/com/google/testing/junit/testparameterinjector/TestParametersMethodProcessor.java @@ -16,7 +16,6 @@ package com.google.testing.junit.testparameterinjector; import static com.google.common.base.Preconditions.checkState; import static com.google.common.base.Verify.verify; -import static com.google.common.collect.Iterables.getOnlyElement; import com.google.auto.value.AutoAnnotation; import com.google.common.base.Optional; @@ -90,7 +89,8 @@ final class TestParametersMethodProcessor implements TestMethodProcessor { @Override public List<TestInfo> calculateTestInfos(TestInfo originalTest) { boolean constructorIsParameterized = - hasRelevantAnnotation(getOnlyConstructor(originalTest.getTestClass())); + hasRelevantAnnotation( + TestParameterInjectorUtils.getOnlyConstructor(originalTest.getTestClass())); boolean methodIsParameterized = hasRelevantAnnotation(originalTest.getMethod()); if (!constructorIsParameterized && !methodIsParameterized) { @@ -148,7 +148,7 @@ final class TestParametersMethodProcessor implements TestMethodProcessor { private ImmutableList<Optional<TestParametersValues>> getConstructorParametersOrSingleAbsentElement(Class<?> testClass) { - Constructor<?> constructor = getOnlyConstructor(testClass); + Constructor<?> constructor = TestParameterInjectorUtils.getOnlyConstructor(testClass); return hasRelevantAnnotation(constructor) ? FluentIterable.from(getConstructorParameters(constructor)) .transform(Optional::of) @@ -444,14 +444,6 @@ final class TestParametersMethodProcessor implements TestMethodProcessor { .toArray(Object.class)); } - private static Constructor<?> getOnlyConstructor(Class<?> testClass) { - ImmutableList<Constructor<?>> constructors = - ImmutableList.copyOf(testClass.getDeclaredConstructors()); - checkState( - constructors.size() == 1, "Expected exactly one constructor, but got %s", constructors); - return getOnlyElement(constructors); - } - /** * This mechanism is a workaround to be able to store the test index in the annotation list of the * {@link TestInfo}, since we cannot carry other information through the test runner. diff --git a/junit4/src/test/java/com/google/testing/junit/testparameterinjector/TestParameterAnnotationMethodProcessorTest.java b/junit4/src/test/java/com/google/testing/junit/testparameterinjector/TestParameterAnnotationMethodProcessorTest.java index 3fff85b..df3a16b 100644 --- a/junit4/src/test/java/com/google/testing/junit/testparameterinjector/TestParameterAnnotationMethodProcessorTest.java +++ b/junit4/src/test/java/com/google/testing/junit/testparameterinjector/TestParameterAnnotationMethodProcessorTest.java @@ -675,6 +675,14 @@ public class TestParameterAnnotationMethodProcessorTest { void test(@TestParameter boolean b) {} } + @ClassTestResult(Result.FAILURE) + public static class ErrorPackagePrivateConstructor { + ErrorPackagePrivateConstructor() {} + + @Test + public void test1() {} + } + public enum EnumA { A1, A2 @@ -829,7 +837,7 @@ public class TestParameterAnnotationMethodProcessorTest { case SUCCESS_FOR_ALL_PLACEMENTS_ONLY: assertThrows( - RuntimeException.class, + Exception.class, () -> SharedTestUtilitiesJUnit4.runTestsAndGetFailures( newTestRunnerWithParameterizedSupport( @@ -838,7 +846,7 @@ public class TestParameterAnnotationMethodProcessorTest { case FAILURE: assertThrows( - RuntimeException.class, + Exception.class, () -> SharedTestUtilitiesJUnit4.runTestsAndGetFailures( newTestRunnerWithParameterizedSupport( diff --git a/junit4/src/test/java/com/google/testing/junit/testparameterinjector/TestParametersMethodProcessorTest.java b/junit4/src/test/java/com/google/testing/junit/testparameterinjector/TestParametersMethodProcessorTest.java index d5417e6..5628330 100644 --- a/junit4/src/test/java/com/google/testing/junit/testparameterinjector/TestParametersMethodProcessorTest.java +++ b/junit4/src/test/java/com/google/testing/junit/testparameterinjector/TestParametersMethodProcessorTest.java @@ -494,6 +494,14 @@ public class TestParametersMethodProcessorTest { public void test1(TestEnum testEnum) {} } + @RunAsTest(failsWithMessage = "Test class should have exactly one public constructor") + public static class InvalidTestBecausePackagePrivateConstructor { + InvalidTestBecausePackagePrivateConstructor() {} + + @Test + public void test1() {} + } + @Parameters(name = "{0}") public static Collection<Object[]> parameters() { return Arrays.stream(TestParametersMethodProcessorTest.class.getClasses()) @@ -527,12 +535,12 @@ public class TestParametersMethodProcessorTest { public void test_failure() throws Exception { assume().that(maybeFailureMessage.isPresent()).isTrue(); - IllegalStateException exception = + Exception exception = assertThrows( - IllegalStateException.class, + Exception.class, () -> SharedTestUtilitiesJUnit4.runTestsAndGetFailures(newTestRunner())); - assertThat(exception).hasMessageThat().isEqualTo(maybeFailureMessage.get()); + assertThat(exception).hasMessageThat().contains(maybeFailureMessage.get()); } private PluggableTestRunner newTestRunner() throws Exception { diff --git a/junit5/src/main/java/com/google/testing/junit/testparameterinjector/junit5/TestParameterAnnotationMethodProcessor.java b/junit5/src/main/java/com/google/testing/junit/testparameterinjector/junit5/TestParameterAnnotationMethodProcessor.java index 834a83d..5aefe21 100644 --- a/junit5/src/main/java/com/google/testing/junit/testparameterinjector/junit5/TestParameterAnnotationMethodProcessor.java +++ b/junit5/src/main/java/com/google/testing/junit/testparameterinjector/junit5/TestParameterAnnotationMethodProcessor.java @@ -867,7 +867,8 @@ final class TestParameterAnnotationMethodProcessor implements TestMethodProcesso case FIELD: // Fall through. case CLASS: return getAnnotationListWithType( - getOnlyConstructor(testClass).getAnnotations(), + TestParameterInjectorUtils.getOnlyConstructor(testClass) + .getAnnotations(), annotationTypeOrigin.annotationType()) .isEmpty(); default: @@ -890,7 +891,7 @@ final class TestParameterAnnotationMethodProcessor implements TestMethodProcesso Origin origin = annotationTypeOrigin.origin(); Class<? extends Annotation> annotationType = annotationTypeOrigin.annotationType(); if (origin == Origin.CONSTRUCTOR_PARAMETER) { - Constructor<?> constructor = getOnlyConstructor(testClass); + Constructor<?> constructor = TestParameterInjectorUtils.getOnlyConstructor(testClass); List<AnnotationWithMetadata> annotations = getAnnotationWithMetadataListWithType(constructor, annotationType); @@ -898,7 +899,8 @@ final class TestParameterAnnotationMethodProcessor implements TestMethodProcesso return toTestParameterValueList(annotations, origin); } } else if (origin == Origin.CONSTRUCTOR) { - Annotation annotation = getOnlyConstructor(testClass).getAnnotation(annotationType); + Annotation annotation = + TestParameterInjectorUtils.getOnlyConstructor(testClass).getAnnotation(annotationType); if (annotation != null) { return ImmutableList.of( TestParameterValueHolder.create( @@ -1022,15 +1024,6 @@ final class TestParameterAnnotationMethodProcessor implements TestMethodProcesso .toList(); } - private static Constructor<?> getOnlyConstructor(Class<?> testClass) { - Constructor<?>[] constructors = testClass.getDeclaredConstructors(); - checkState( - constructors.length == 1, - "a single public constructor is required for class %s", - testClass); - return constructors[0]; - } - @Override public void postProcessTestInstance(Object testInstance, TestInfo testInfo) { TestIndexHolder testIndexHolder = testInfo.getAnnotation(TestIndexHolder.class); diff --git a/junit5/src/main/java/com/google/testing/junit/testparameterinjector/junit5/TestParameterInjectorUtils.java b/junit5/src/main/java/com/google/testing/junit/testparameterinjector/junit5/TestParameterInjectorUtils.java new file mode 100644 index 0000000..cd47ea1 --- /dev/null +++ b/junit5/src/main/java/com/google/testing/junit/testparameterinjector/junit5/TestParameterInjectorUtils.java @@ -0,0 +1,47 @@ +/* + * Copyright 2023 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package com.google.testing.junit.testparameterinjector.junit5; + +import static com.google.common.base.Preconditions.checkState; +import static com.google.common.collect.Iterables.getOnlyElement; + +import com.google.common.collect.ImmutableList; +import java.lang.reflect.Constructor; + +/** Shared utility methods. */ +class TestParameterInjectorUtils { + + /** + * Return the only public constructor of the given test class. If there is none, return the only + * constructor. + * + * <p>Normally, there should be exactly one constructor (public or other), but some frameworks + * introduce an extra non-public constructor (see + * https://github.com/google/TestParameterInjector/issues/40). + */ + static Constructor<?> getOnlyConstructor(Class<?> testClass) { + ImmutableList<Constructor<?>> constructors = ImmutableList.copyOf(testClass.getConstructors()); + if (constructors.isEmpty()) { + // There are no public constructors. This is likely a JUnit5 test, so we should take the only + // non-public constructor instead. + constructors = ImmutableList.copyOf(testClass.getDeclaredConstructors()); + } + checkState( + constructors.size() == 1, "Expected exactly one constructor, but got %s", constructors); + return getOnlyElement(constructors); + } + + private TestParameterInjectorUtils() {} +} diff --git a/junit5/src/main/java/com/google/testing/junit/testparameterinjector/junit5/TestParametersMethodProcessor.java b/junit5/src/main/java/com/google/testing/junit/testparameterinjector/junit5/TestParametersMethodProcessor.java index 3ada177..26a1e65 100644 --- a/junit5/src/main/java/com/google/testing/junit/testparameterinjector/junit5/TestParametersMethodProcessor.java +++ b/junit5/src/main/java/com/google/testing/junit/testparameterinjector/junit5/TestParametersMethodProcessor.java @@ -16,7 +16,6 @@ package com.google.testing.junit.testparameterinjector.junit5; import static com.google.common.base.Preconditions.checkState; import static com.google.common.base.Verify.verify; -import static com.google.common.collect.Iterables.getOnlyElement; import com.google.auto.value.AutoAnnotation; import com.google.common.base.Optional; @@ -90,7 +89,8 @@ final class TestParametersMethodProcessor implements TestMethodProcessor { @Override public List<TestInfo> calculateTestInfos(TestInfo originalTest) { boolean constructorIsParameterized = - hasRelevantAnnotation(getOnlyConstructor(originalTest.getTestClass())); + hasRelevantAnnotation( + TestParameterInjectorUtils.getOnlyConstructor(originalTest.getTestClass())); boolean methodIsParameterized = hasRelevantAnnotation(originalTest.getMethod()); if (!constructorIsParameterized && !methodIsParameterized) { @@ -148,7 +148,7 @@ final class TestParametersMethodProcessor implements TestMethodProcessor { private ImmutableList<Optional<TestParametersValues>> getConstructorParametersOrSingleAbsentElement(Class<?> testClass) { - Constructor<?> constructor = getOnlyConstructor(testClass); + Constructor<?> constructor = TestParameterInjectorUtils.getOnlyConstructor(testClass); return hasRelevantAnnotation(constructor) ? FluentIterable.from(getConstructorParameters(constructor)) .transform(Optional::of) @@ -444,14 +444,6 @@ final class TestParametersMethodProcessor implements TestMethodProcessor { .toArray(Object.class)); } - private static Constructor<?> getOnlyConstructor(Class<?> testClass) { - ImmutableList<Constructor<?>> constructors = - ImmutableList.copyOf(testClass.getDeclaredConstructors()); - checkState( - constructors.size() == 1, "Expected exactly one constructor, but got %s", constructors); - return getOnlyElement(constructors); - } - /** * This mechanism is a workaround to be able to store the test index in the annotation list of the * {@link TestInfo}, since we cannot carry other information through the test runner. |