TSK-1941: test-api injects enclosing class instance for non-static member classes

this allows SPIs to use instance fields from their enclosing class
This commit is contained in:
Mustapha Zorgati 2022-08-11 00:24:25 +02:00
parent 41ecfa2245
commit 830723800e
6 changed files with 243 additions and 96 deletions

View File

@ -44,4 +44,18 @@ public class ReflectionUtil {
public static <T> Class<T> wrap(Class<T> c) {
return c.isPrimitive() ? (Class<T>) PRIMITIVES_TO_WRAPPERS.get(c) : c;
}
public static Object getEnclosingInstance(Object instance) {
return Arrays.stream(instance.getClass().getDeclaredFields())
.filter(Field::isSynthetic)
.filter(f -> f.getName().startsWith("this"))
.findFirst()
.map(
CheckedFunction.wrap(
field -> {
field.setAccessible(true);
return field.get(instance);
}))
.orElse(null);
}
}

View File

@ -6,6 +6,9 @@ import java.lang.reflect.Field;
import java.util.List;
import org.junit.jupiter.api.Test;
import pro.taskana.common.internal.util.TopLevelTestClass.FirstNestedClass;
import pro.taskana.common.internal.util.TopLevelTestClass.FirstNestedClass.SecondNestedClass;
class ReflectionUtilTest {
@Test
@ -31,6 +34,36 @@ class ReflectionUtilTest {
assertThat(wrap).isEqualTo(TestClass.class);
}
@Test
void should_ReturnNull_For_TopLevelClass() {
TopLevelTestClass topLevelTestClass = new TopLevelTestClass();
Object enclosingInstance = ReflectionUtil.getEnclosingInstance(topLevelTestClass);
assertThat(enclosingInstance).isNull();
}
@Test
void should_ReturnTopLevelInstance_For_NestedInstance() {
TopLevelTestClass topLevelTestClass = new TopLevelTestClass();
FirstNestedClass firstNestedClass = topLevelTestClass.new FirstNestedClass();
Object enclosingInstance = ReflectionUtil.getEnclosingInstance(firstNestedClass);
assertThat(enclosingInstance).isSameAs(topLevelTestClass);
}
@Test
void should_ReturnNestedInstance_For_NestedNestedInstance() {
TopLevelTestClass topLevelTestClass = new TopLevelTestClass();
FirstNestedClass firstNestedClass = topLevelTestClass.new FirstNestedClass();
SecondNestedClass secondNestedClass = firstNestedClass.new SecondNestedClass();
Object enclosingInstance = ReflectionUtil.getEnclosingInstance(secondNestedClass);
assertThat(enclosingInstance).isSameAs(firstNestedClass);
}
static class TestClass {
@SuppressWarnings("unused")
String fieldA;
@ -46,3 +79,16 @@ class ReflectionUtilTest {
String fieldC;
}
}
@SuppressWarnings({"checkstyle:OneTopLevelClass", "InnerClassMayBeStatic", "unused"})
class TopLevelTestClass {
String someField;
class FirstNestedClass {
String someField;
class SecondNestedClass {
String someField;
}
}
}

View File

@ -55,8 +55,6 @@ class TaskRoutingAccTest {
.permission(WorkbasketPermission.READ)
.permission(WorkbasketPermission.APPEND)
.buildAndStore(workbasketService);
TaskRoutingProviderForDomainA.domainAWorkbasketId = domainAWorkbasket.getId();
}
@WithAccessId(user = "user-1-1")
@ -83,9 +81,7 @@ class TaskRoutingAccTest {
assertThat(createdTask.getWorkbasketSummary()).isEqualTo(domainAWorkbasket);
}
public static class TaskRoutingProviderForDomainA implements TaskRoutingProvider {
static String domainAWorkbasketId;
class TaskRoutingProviderForDomainA implements TaskRoutingProvider {
@Override
public void initialize(TaskanaEngine taskanaEngine) {}
@ -93,7 +89,7 @@ class TaskRoutingAccTest {
@Override
public String determineWorkbasketId(Task task) {
if ("DOMAIN_A".equals(task.getDomain())) {
return domainAWorkbasketId;
return domainAWorkbasket.getId();
}
return null;
}

View File

@ -4,6 +4,7 @@ import static org.junit.platform.commons.support.AnnotationSupport.isAnnotated;
import static pro.taskana.testapi.util.ExtensionCommunicator.getClassLevelStore;
import static pro.taskana.testapi.util.ExtensionCommunicator.isTopLevelClass;
import java.util.HashMap;
import java.util.Map;
import javax.sql.DataSource;
import org.apache.ibatis.session.SqlSession;
@ -29,6 +30,7 @@ import pro.taskana.common.internal.InternalTaskanaEngine;
import pro.taskana.common.internal.JobServiceImpl;
import pro.taskana.common.internal.TaskanaEngineImpl;
import pro.taskana.common.internal.security.CurrentUserContextImpl;
import pro.taskana.common.internal.util.ReflectionUtil;
import pro.taskana.common.internal.util.SpiLoader;
import pro.taskana.monitor.api.MonitorService;
import pro.taskana.monitor.internal.MonitorServiceImpl;
@ -68,7 +70,8 @@ public class TaskanaInitializationExtension implements TestInstancePostProcessor
TaskanaEngine taskanaEngine;
try (MockedStatic<SpiLoader> staticMock = Mockito.mockStatic(SpiLoader.class)) {
ServiceProviderExtractor.extractServiceProviders(testClass)
ServiceProviderExtractor.extractServiceProviders(
testClass, extractEnclosingTestInstances(testInstance))
.forEach(
(spi, serviceProviders) ->
staticMock.when(() -> SpiLoader.load(spi)).thenReturn(serviceProviders));
@ -80,6 +83,15 @@ public class TaskanaInitializationExtension implements TestInstancePostProcessor
}
}
private static Map<Class<?>, Object> extractEnclosingTestInstances(Object instance) {
HashMap<Class<?>, Object> instanceByClass = new HashMap<>();
while (instance != null) {
instanceByClass.put(instance.getClass(), instance);
instance = ReflectionUtil.getEnclosingInstance(instance);
}
return instanceByClass;
}
private static TaskanaEngineConfiguration createDefaultTaskanaEngineConfiguration(Store store) {
String schemaName = store.get(TestContainerExtension.STORE_SCHEMA_NAME, String.class);
if (schemaName == null) {

View File

@ -1,9 +1,7 @@
package pro.taskana.testapi.util;
import static org.junit.platform.commons.support.AnnotationSupport.findRepeatableAnnotations;
import static pro.taskana.common.internal.util.CheckedFunction.wrap;
import java.lang.reflect.Constructor;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.List;
@ -12,6 +10,7 @@ import java.util.Map.Entry;
import java.util.Set;
import java.util.stream.Collectors;
import org.junit.platform.commons.JUnitException;
import org.junit.platform.commons.support.ReflectionSupport;
import pro.taskana.spi.history.api.TaskanaHistory;
import pro.taskana.spi.priority.api.PriorityServiceProvider;
@ -36,7 +35,8 @@ public class ServiceProviderExtractor {
throw new IllegalStateException("utility class");
}
public static Map<Class<?>, List<Object>> extractServiceProviders(Class<?> testClass) {
public static Map<Class<?>, List<Object>> extractServiceProviders(
Class<?> testClass, Map<Class<?>, Object> enclosingTestInstancesByClass) {
List<WithServiceProvider> withServiceProviders =
findRepeatableAnnotations(testClass, WithServiceProvider.class);
@ -44,7 +44,9 @@ public class ServiceProviderExtractor {
.peek(entry -> validateServiceProviders(entry.getKey(), entry.getValue()))
.collect(
Collectors.toMap(
Entry::getKey, entry -> instantiateServiceProviders(entry.getValue())));
Entry::getKey,
entry ->
instantiateServiceProviders(entry.getValue(), enclosingTestInstancesByClass)));
}
private static void validateServiceProviders(Class<?> spi, List<Class<?>> serviceProviders) {
@ -69,24 +71,33 @@ public class ServiceProviderExtractor {
Collectors.toList())));
}
private static List<Object> instantiateServiceProviders(List<Class<?>> serviceProviders) {
private static List<Object> instantiateServiceProviders(
List<Class<?>> serviceProviders, Map<Class<?>, Object> enclosingTestInstancesByClass) {
return serviceProviders.stream()
.map(wrap(ServiceProviderExtractor::instantiateClass))
.map(clz -> instantiateClass(clz, enclosingTestInstancesByClass))
.collect(Collectors.toList());
}
private static Object instantiateClass(Class<?> clz) throws Exception {
private static Object instantiateClass(
Class<?> clz, Map<Class<?>, Object> enclosingTestInstancesByClass) {
// we don't have to consider anonymous classes since they can't be passed as an argument to
// the WithServiceProvider annotation.
if (clz.isLocalClass() || (clz.isMemberClass() && !Modifier.isStatic(clz.getModifiers()))) {
Class<?> motherClass = clz.getEnclosingClass();
Object motherInstance = instantiateClass(motherClass);
Constructor<?> constructor = clz.getDeclaredConstructor(motherClass);
constructor.setAccessible(true);
return constructor.newInstance(motherInstance);
try {
Class<?> motherClass = clz.getEnclosingClass();
Object motherInstance =
enclosingTestInstancesByClass.getOrDefault(
motherClass, instantiateClass(motherClass, enclosingTestInstancesByClass));
return ReflectionSupport.newInstance(clz, motherInstance);
} catch (Exception e) {
//noinspection ConstantConditions
if (NoSuchMethodException.class == e.getClass()) {
throw new JUnitException(
"test-api does not support local class which accesses method variables");
}
throw e;
}
}
Constructor<?> constructor = clz.getDeclaredConstructor();
constructor.setAccessible(true);
return constructor.newInstance();
return ReflectionSupport.newInstance(clz);
}
}

View File

@ -1,5 +1,6 @@
package pro.taskana.testapi.util;
import static java.util.Map.entry;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static pro.taskana.testapi.util.ServiceProviderExtractor.extractServiceProviders;
@ -14,6 +15,7 @@ import org.junit.jupiter.api.TestInstance;
import org.junit.jupiter.api.TestInstance.Lifecycle;
import org.junit.platform.commons.JUnitException;
import pro.taskana.common.internal.util.ReflectionUtil;
import pro.taskana.spi.priority.api.PriorityServiceProvider;
import pro.taskana.spi.task.api.CreateTaskPreprocessor;
import pro.taskana.task.api.models.Task;
@ -66,16 +68,30 @@ class ServiceProviderExtractorTest {
}
}
@SuppressWarnings("InnerClassMayBeStatic")
class NonStaticCreateTaskPreprocessorOutsideOfTestClass implements CreateTaskPreprocessor {
@Override
public void processTaskBeforeCreation(Task taskToProcess) {
// implementation not important for the tests
}
}
@Nested
@TestInstance(Lifecycle.PER_CLASS)
class ServiceProviderInstantiation {
private final Map<Class<?>, Object> enclosingTestInstancesByClass =
Map.ofEntries(
entry(ServiceProviderInstantiation.class, this),
entry(ServiceProviderExtractorTest.class, ServiceProviderExtractorTest.this));
@Test
void should_ReturnEmptyMap_When_NoServiceProviderIsDefined() {
class ExampleClazzWithNoServiceProviders {}
class ExampleTestClassWithNoServiceProviders {}
Map<Class<?>, List<Object>> extractServiceProviders =
extractServiceProviders(ExampleClazzWithNoServiceProviders.class);
extractServiceProviders(
ExampleTestClassWithNoServiceProviders.class, enclosingTestInstancesByClass);
assertThat(extractServiceProviders).isEmpty();
}
@ -85,16 +101,17 @@ class ServiceProviderExtractorTest {
@WithServiceProvider(
serviceProviderInterface = CreateTaskPreprocessor.class,
serviceProviders = TopLevelCreateTaskPreprocessor.class)
class ExampleClassWithServiceProviders {}
class ExampleTestClassWithServiceProviders {}
Map<Class<?>, List<Object>> extractServiceProviders =
extractServiceProviders(ExampleClassWithServiceProviders.class);
extractServiceProviders(
ExampleTestClassWithServiceProviders.class, enclosingTestInstancesByClass);
assertThat(extractServiceProviders).containsOnlyKeys(CreateTaskPreprocessor.class);
assertThat(extractServiceProviders.get(CreateTaskPreprocessor.class))
.extracting(Object::getClass)
.asList()
.containsExactly(TopLevelCreateTaskPreprocessor.class);
.hasExactlyElementsOfTypes(TopLevelCreateTaskPreprocessor.class)
.extracting(ReflectionUtil::getEnclosingInstance)
.containsOnlyNulls();
}
@Test
@ -102,16 +119,17 @@ class ServiceProviderExtractorTest {
@WithServiceProvider(
serviceProviderInterface = CreateTaskPreprocessor.class,
serviceProviders = StaticCreateTaskPreprocessor.class)
class ExampleClassWithServiceProviders {}
class ExampleTestClassWithServiceProviders {}
Map<Class<?>, List<Object>> extractServiceProviders =
extractServiceProviders(ExampleClassWithServiceProviders.class);
extractServiceProviders(
ExampleTestClassWithServiceProviders.class, enclosingTestInstancesByClass);
assertThat(extractServiceProviders).containsOnlyKeys(CreateTaskPreprocessor.class);
assertThat(extractServiceProviders.get(CreateTaskPreprocessor.class))
.extracting(Object::getClass)
.asList()
.containsExactly(StaticCreateTaskPreprocessor.class);
.hasExactlyElementsOfTypes(StaticCreateTaskPreprocessor.class)
.extracting(ReflectionUtil::getEnclosingInstance)
.containsOnlyNulls();
}
@Test
@ -119,16 +137,17 @@ class ServiceProviderExtractorTest {
@WithServiceProvider(
serviceProviderInterface = CreateTaskPreprocessor.class,
serviceProviders = PrivateStaticCreateTaskPreprocessor.class)
class ExampleClassWithServiceProviders {}
class ExampleTestClassWithServiceProviders {}
Map<Class<?>, List<Object>> extractServiceProviders =
extractServiceProviders(ExampleClassWithServiceProviders.class);
extractServiceProviders(
ExampleTestClassWithServiceProviders.class, enclosingTestInstancesByClass);
assertThat(extractServiceProviders).containsOnlyKeys(CreateTaskPreprocessor.class);
assertThat(extractServiceProviders.get(CreateTaskPreprocessor.class))
.extracting(Object::getClass)
.asList()
.containsExactly(PrivateStaticCreateTaskPreprocessor.class);
.hasExactlyElementsOfTypes(PrivateStaticCreateTaskPreprocessor.class)
.extracting(ReflectionUtil::getEnclosingInstance)
.containsOnlyNulls();
}
@Test
@ -143,33 +162,35 @@ class ServiceProviderExtractorTest {
@WithServiceProvider(
serviceProviderInterface = CreateTaskPreprocessor.class,
serviceProviders = LocalCreateTaskPreprocessor.class)
class ExampleClassWithServiceProviders {}
class ExampleTestClassWithServiceProviders {}
Map<Class<?>, List<Object>> extractServiceProviders =
extractServiceProviders(ExampleClassWithServiceProviders.class);
extractServiceProviders(
ExampleTestClassWithServiceProviders.class, enclosingTestInstancesByClass);
assertThat(extractServiceProviders).containsOnlyKeys(CreateTaskPreprocessor.class);
assertThat(extractServiceProviders.get(CreateTaskPreprocessor.class))
.extracting(Object::getClass)
.asList()
.containsExactly(LocalCreateTaskPreprocessor.class);
.hasExactlyElementsOfTypes(LocalCreateTaskPreprocessor.class)
.extracting(ReflectionUtil::getEnclosingInstance)
.containsExactly(this);
}
@Test
void should_InstantiateServiceProvider_When_ServiceProviderIsNonStaticMemberClass() {
@WithServiceProvider(
serviceProviderInterface = CreateTaskPreprocessor.class,
serviceProviders = NonStaticCreateTaskPreprocessor.class)
class ExampleClassWithServiceProviders {}
serviceProviders = NonStaticCreateTaskPreprocessorInsideOfTestClass.class)
class ExampleTestClassWithServiceProviders {}
Map<Class<?>, List<Object>> extractServiceProviders =
extractServiceProviders(ExampleClassWithServiceProviders.class);
extractServiceProviders(
ExampleTestClassWithServiceProviders.class, enclosingTestInstancesByClass);
assertThat(extractServiceProviders).containsOnlyKeys(CreateTaskPreprocessor.class);
assertThat(extractServiceProviders.get(CreateTaskPreprocessor.class))
.extracting(Object::getClass)
.asList()
.containsExactly(NonStaticCreateTaskPreprocessor.class);
.hasExactlyElementsOfTypes(NonStaticCreateTaskPreprocessorInsideOfTestClass.class)
.extracting(ReflectionUtil::getEnclosingInstance)
.containsExactly(this);
}
@Test
@ -177,20 +198,38 @@ class ServiceProviderExtractorTest {
@WithServiceProvider(
serviceProviderInterface = CreateTaskPreprocessor.class,
serviceProviders = PrivateNonStaticCreateTaskPreprocessor.class)
class ExampleClassWithServiceProviders {}
class ExampleTestClassWithServiceProviders {}
Map<Class<?>, List<Object>> extractServiceProviders =
extractServiceProviders(ExampleClassWithServiceProviders.class);
extractServiceProviders(
ExampleTestClassWithServiceProviders.class, enclosingTestInstancesByClass);
assertThat(extractServiceProviders).containsOnlyKeys(CreateTaskPreprocessor.class);
assertThat(extractServiceProviders.get(CreateTaskPreprocessor.class))
.extracting(Object::getClass)
.asList()
.containsExactly(PrivateNonStaticCreateTaskPreprocessor.class);
.hasExactlyElementsOfTypes(PrivateNonStaticCreateTaskPreprocessor.class)
.extracting(ReflectionUtil::getEnclosingInstance)
.containsExactly(this);
}
class NonStaticCreateTaskPreprocessor implements CreateTaskPreprocessor {
@Test
void should_InstantiateServiceProvider_When_ItIsNonStaticMemberClassOutsideOfTestClass() {
@WithServiceProvider(
serviceProviderInterface = CreateTaskPreprocessor.class,
serviceProviders = NonStaticCreateTaskPreprocessorOutsideOfTestClass.class)
class ExampleTestClassWithServiceProviders {}
Map<Class<?>, List<Object>> extractServiceProviders =
extractServiceProviders(
ExampleTestClassWithServiceProviders.class, enclosingTestInstancesByClass);
assertThat(extractServiceProviders).containsOnlyKeys(CreateTaskPreprocessor.class);
assertThat(extractServiceProviders.get(CreateTaskPreprocessor.class))
.hasExactlyElementsOfTypes(NonStaticCreateTaskPreprocessorOutsideOfTestClass.class)
.extracting(ReflectionUtil::getEnclosingInstance)
.containsExactly(ServiceProviderExtractorTest.this);
}
class NonStaticCreateTaskPreprocessorInsideOfTestClass implements CreateTaskPreprocessor {
@Override
public void processTaskBeforeCreation(Task taskToProcess) {
// implementation not important for the tests
@ -210,21 +249,26 @@ class ServiceProviderExtractorTest {
@TestInstance(Lifecycle.PER_CLASS)
class ExtractServiceProvidersFromSingleServiceProviderInterface {
private final Map<Class<?>, Object> enclosingTestInstancesByClass =
Map.ofEntries(
entry(ExtractServiceProvidersFromSingleServiceProviderInterface.class, this),
entry(ServiceProviderExtractorTest.class, ServiceProviderExtractorTest.this));
@Test
void should_ExtractServiceProvider() {
@WithServiceProvider(
serviceProviderInterface = CreateTaskPreprocessor.class,
serviceProviders = DummyTaskPreprocessor1.class)
class ExampleClassWithServiceProviders {}
class ExampleTestClassWithServiceProviders {}
Map<Class<?>, List<Object>> extractServiceProviders =
extractServiceProviders(ExampleClassWithServiceProviders.class);
extractServiceProviders(
ExampleTestClassWithServiceProviders.class, enclosingTestInstancesByClass);
assertThat(extractServiceProviders).containsOnlyKeys(CreateTaskPreprocessor.class);
assertThat(extractServiceProviders.get(CreateTaskPreprocessor.class))
.extracting(Object::getClass)
.asList()
.containsExactly(DummyTaskPreprocessor1.class);
.hasExactlyElementsOfTypes(DummyTaskPreprocessor1.class);
}
@Test
@ -232,16 +276,16 @@ class ServiceProviderExtractorTest {
@WithServiceProvider(
serviceProviderInterface = CreateTaskPreprocessor.class,
serviceProviders = {DummyTaskPreprocessor1.class, DummyTaskPreprocessor2.class})
class ExampleClassWithServiceProviders {}
class ExampleTestClassWithServiceProviders {}
Map<Class<?>, List<Object>> extractServiceProviders =
extractServiceProviders(ExampleClassWithServiceProviders.class);
extractServiceProviders(
ExampleTestClassWithServiceProviders.class, enclosingTestInstancesByClass);
assertThat(extractServiceProviders).containsOnlyKeys(CreateTaskPreprocessor.class);
assertThat(extractServiceProviders.get(CreateTaskPreprocessor.class))
.extracting(Object::getClass)
.asList()
.containsExactly(DummyTaskPreprocessor1.class, DummyTaskPreprocessor2.class);
.hasExactlyElementsOfTypes(DummyTaskPreprocessor1.class, DummyTaskPreprocessor2.class);
}
@Test
@ -252,16 +296,15 @@ class ServiceProviderExtractorTest {
@WithServiceProvider(
serviceProviderInterface = CreateTaskPreprocessor.class,
serviceProviders = DummyTaskPreprocessor2.class)
class ExampleClassWithServiceProviders {}
class ExampleTestClassWithServiceProviders {}
Map<Class<?>, List<Object>> extractServiceProviders =
extractServiceProviders(ExampleClassWithServiceProviders.class);
extractServiceProviders(
ExampleTestClassWithServiceProviders.class, enclosingTestInstancesByClass);
assertThat(extractServiceProviders).containsOnlyKeys(CreateTaskPreprocessor.class);
assertThat(extractServiceProviders.get(CreateTaskPreprocessor.class))
.extracting(Object::getClass)
.asList()
.containsExactly(DummyTaskPreprocessor1.class, DummyTaskPreprocessor2.class);
.hasExactlyElementsOfTypes(DummyTaskPreprocessor1.class, DummyTaskPreprocessor2.class);
}
@Test
@ -269,16 +312,15 @@ class ServiceProviderExtractorTest {
@WithServiceProvider(
serviceProviderInterface = CreateTaskPreprocessor.class,
serviceProviders = {DummyTaskPreprocessor1.class, DummyTaskPreprocessor1.class})
class ExampleClassWithServiceProviders {}
class ExampleTestClassWithServiceProviders {}
Map<Class<?>, List<Object>> extractServiceProviders =
extractServiceProviders(ExampleClassWithServiceProviders.class);
extractServiceProviders(
ExampleTestClassWithServiceProviders.class, enclosingTestInstancesByClass);
assertThat(extractServiceProviders).containsOnlyKeys(CreateTaskPreprocessor.class);
assertThat(extractServiceProviders.get(CreateTaskPreprocessor.class))
.extracting(Object::getClass)
.asList()
.containsExactly(DummyTaskPreprocessor1.class, DummyTaskPreprocessor1.class);
.hasExactlyElementsOfTypes(DummyTaskPreprocessor1.class, DummyTaskPreprocessor1.class);
}
}
@ -286,6 +328,11 @@ class ServiceProviderExtractorTest {
@TestInstance(Lifecycle.PER_CLASS)
class ExtractMultipleServiceProvidersFromMultipleServiceProviderInterfaces {
private final Map<Class<?>, Object> enclosingTestInstancesByClass =
Map.ofEntries(
entry(ExtractMultipleServiceProvidersFromMultipleServiceProviderInterfaces.class, this),
entry(ServiceProviderExtractorTest.class, ServiceProviderExtractorTest.this));
@Test
void should_ExtractServiceProviders() {
@WithServiceProvider(
@ -294,21 +341,18 @@ class ServiceProviderExtractorTest {
@WithServiceProvider(
serviceProviderInterface = PriorityServiceProvider.class,
serviceProviders = DummyPriorityServiceProvider1.class)
class ExampleClassWithServiceProviders {}
class ExampleTestClassWithServiceProviders {}
Map<Class<?>, List<Object>> extractServiceProviders =
extractServiceProviders(ExampleClassWithServiceProviders.class);
extractServiceProviders(
ExampleTestClassWithServiceProviders.class, enclosingTestInstancesByClass);
assertThat(extractServiceProviders)
.containsOnlyKeys(CreateTaskPreprocessor.class, PriorityServiceProvider.class);
assertThat(extractServiceProviders.get(CreateTaskPreprocessor.class))
.extracting(Object::getClass)
.asList()
.containsExactly(DummyTaskPreprocessor1.class);
.hasExactlyElementsOfTypes(DummyTaskPreprocessor1.class);
assertThat(extractServiceProviders.get(PriorityServiceProvider.class))
.extracting(Object::getClass)
.asList()
.containsExactly(DummyPriorityServiceProvider1.class);
.hasExactlyElementsOfTypes(DummyPriorityServiceProvider1.class);
}
@Test
@ -322,21 +366,18 @@ class ServiceProviderExtractorTest {
DummyPriorityServiceProvider1.class,
DummyPriorityServiceProvider2.class
})
class ExampleClassWithServiceProviders {}
class ExampleTestClassWithServiceProviders {}
Map<Class<?>, List<Object>> extractServiceProviders =
extractServiceProviders(ExampleClassWithServiceProviders.class);
extractServiceProviders(
ExampleTestClassWithServiceProviders.class, enclosingTestInstancesByClass);
assertThat(extractServiceProviders)
.containsOnlyKeys(CreateTaskPreprocessor.class, PriorityServiceProvider.class);
assertThat(extractServiceProviders.get(CreateTaskPreprocessor.class))
.extracting(Object::getClass)
.asList()
.containsExactly(DummyTaskPreprocessor1.class, DummyTaskPreprocessor2.class);
.hasExactlyElementsOfTypes(DummyTaskPreprocessor1.class, DummyTaskPreprocessor2.class);
assertThat(extractServiceProviders.get(PriorityServiceProvider.class))
.extracting(Object::getClass)
.asList()
.containsExactly(
.hasExactlyElementsOfTypes(
DummyPriorityServiceProvider1.class, DummyPriorityServiceProvider2.class);
}
}
@ -350,9 +391,10 @@ class ServiceProviderExtractorTest {
@WithServiceProvider(
serviceProviderInterface = ErrorHandling.class,
serviceProviders = DummyTaskPreprocessor1.class)
class ExampleClassWithServiceProviders {}
class ExampleTestClassWithServiceProviders {}
ThrowingCallable call = () -> extractServiceProviders(ExampleClassWithServiceProviders.class);
ThrowingCallable call =
() -> extractServiceProviders(ExampleTestClassWithServiceProviders.class, Map.of());
assertThatThrownBy(call)
.isInstanceOf(JUnitException.class)
@ -364,9 +406,10 @@ class ServiceProviderExtractorTest {
@WithServiceProvider(
serviceProviderInterface = CreateTaskPreprocessor.class,
serviceProviders = DummyPriorityServiceProvider1.class)
class ExampleClassWithServiceProviders {}
class ExampleTestClassWithServiceProviders {}
ThrowingCallable call = () -> extractServiceProviders(ExampleClassWithServiceProviders.class);
ThrowingCallable call =
() -> extractServiceProviders(ExampleTestClassWithServiceProviders.class, Map.of());
assertThatThrownBy(call)
.isInstanceOf(JUnitException.class)
@ -380,9 +423,10 @@ class ServiceProviderExtractorTest {
@WithServiceProvider(
serviceProviderInterface = CreateTaskPreprocessor.class,
serviceProviders = {DummyTaskPreprocessor1.class, DummyPriorityServiceProvider1.class})
class ExampleClassWithServiceProviders {}
class ExampleTestClassWithServiceProviders {}
ThrowingCallable call = () -> extractServiceProviders(ExampleClassWithServiceProviders.class);
ThrowingCallable call =
() -> extractServiceProviders(ExampleTestClassWithServiceProviders.class, Map.of());
assertThatThrownBy(call)
.isInstanceOf(JUnitException.class)
@ -390,5 +434,29 @@ class ServiceProviderExtractorTest {
"At least one ServiceProvider does not implement the requested SPI '%s'",
CreateTaskPreprocessor.class);
}
@Test
void should_ThrowException_When_LocalServiceProviderUsesAMethodVariable() {
String methodVariable = "foobar";
class LocalCreateTaskPreprocessor implements CreateTaskPreprocessor {
@Override
public void processTaskBeforeCreation(Task taskToProcess) {
// implementation not important for the tests
taskToProcess.setOwner(methodVariable);
}
}
@WithServiceProvider(
serviceProviderInterface = CreateTaskPreprocessor.class,
serviceProviders = LocalCreateTaskPreprocessor.class)
class ExampleTestClassWithServiceProviders {}
ThrowingCallable call =
() -> extractServiceProviders(ExampleTestClassWithServiceProviders.class, Map.of());
assertThatThrownBy(call)
.isInstanceOf(JUnitException.class)
.hasMessage("test-api does not support local class which accesses method variables");
}
}
}