TSK-1937: test-api now supports instantiation off all class types

except for anonymous classes since they're not assignable to the WithAccessId annotation
This commit is contained in:
Mustapha Zorgati 2022-08-10 09:50:00 +02:00
parent 63315eaf22
commit f7b668835c
4 changed files with 426 additions and 5 deletions

View File

@ -166,7 +166,7 @@ public class JaasExtension implements InvocationInterceptor, TestTemplateInvocat
DynamicTestInvocationContext invocationContext,
ExtensionContext extensionContext) {
ExtensionContext testContext = getParentMethodExtensionContent(extensionContext);
// Check if the test factory provided an access Id for this dynamic test.
// Check if the test factory provided an accessId for this dynamic test.
WithAccessId o = getMethodLevelStore(testContext).get(ACCESS_IDS_STORE_KEY, WithAccessId.class);
if (o != null) {
performInvocationWithAccessId(invocation, o);
@ -269,11 +269,11 @@ public class JaasExtension implements InvocationInterceptor, TestTemplateInvocat
Optional<ExtensionContext> parent = extensionContext.getParent();
// the class MethodExtensionContext is part of junit-jupiter-engine and has only a
// package-private visibility thus this workaround is needed.
while (!parent
while (parent
.map(Object::getClass)
.map(Class::getName)
.filter(s -> s.endsWith("MethodExtensionContext"))
.isPresent()) {
.isEmpty()) {
parent = parent.flatMap(ExtensionContext::getParent);
}
return parent.orElseThrow(

View File

@ -4,6 +4,7 @@ import static org.junit.platform.commons.support.AnnotationSupport.findRepeatabl
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;
import java.util.Map;
@ -66,8 +67,22 @@ public class ServiceProviderExtractor {
private static List<Object> instantiateServiceProviders(List<Class<?>> serviceProviders) {
return serviceProviders.stream()
.map(wrap(Class::getDeclaredConstructor))
.map(wrap(Constructor::newInstance))
.map(wrap(ServiceProviderExtractor::instantiateClass))
.collect(Collectors.toList());
}
private static Object instantiateClass(Class<?> clz) throws Exception {
// 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);
}
Constructor<?> constructor = clz.getDeclaredConstructor();
constructor.setAccessible(true);
return constructor.newInstance();
}
}

View File

@ -0,0 +1,394 @@
package pro.taskana.testapi.util;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static pro.taskana.testapi.util.ServiceProviderExtractor.extractServiceProviders;
import java.util.List;
import java.util.Map;
import java.util.OptionalInt;
import org.assertj.core.api.ThrowableAssert.ThrowingCallable;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInstance;
import org.junit.jupiter.api.TestInstance.Lifecycle;
import org.junit.platform.commons.JUnitException;
import pro.taskana.spi.priority.api.PriorityServiceProvider;
import pro.taskana.spi.task.api.CreateTaskPreprocessor;
import pro.taskana.task.api.models.Task;
import pro.taskana.task.api.models.TaskSummary;
import pro.taskana.testapi.WithServiceProvider;
class ServiceProviderExtractorTest {
static class StaticCreateTaskPreprocessor implements CreateTaskPreprocessor {
@Override
public void processTaskBeforeCreation(Task taskToProcess) {
// implementation not important for the tests
}
}
static class DummyTaskPreprocessor1 implements CreateTaskPreprocessor {
@Override
public void processTaskBeforeCreation(Task taskToProcess) {
// implementation not important for the tests
}
}
static class DummyTaskPreprocessor2 implements CreateTaskPreprocessor {
@Override
public void processTaskBeforeCreation(Task taskToProcess) {
// implementation not important for the tests
}
}
static class DummyPriorityServiceProvider1 implements PriorityServiceProvider {
@Override
public OptionalInt calculatePriority(TaskSummary taskSummary) {
// implementation not important for the tests
return OptionalInt.empty();
}
}
static class DummyPriorityServiceProvider2 implements PriorityServiceProvider {
@Override
public OptionalInt calculatePriority(TaskSummary taskSummary) {
// implementation not important for the tests
return OptionalInt.empty();
}
}
private static class PrivateStaticCreateTaskPreprocessor implements CreateTaskPreprocessor {
@Override
public void processTaskBeforeCreation(Task taskToProcess) {
// implementation not important for the tests
}
}
@Nested
@TestInstance(Lifecycle.PER_CLASS)
class ServiceProviderInstantiation {
@Test
void should_ReturnEmptyMap_When_NoServiceProviderIsDefined() {
class ExampleClazzWithNoServiceProviders {}
Map<Class<?>, List<Object>> extractServiceProviders =
extractServiceProviders(ExampleClazzWithNoServiceProviders.class);
assertThat(extractServiceProviders).isEmpty();
}
@Test
void should_InstantiateServiceProvider_When_ServiceProviderIsTopLevelClass() {
@WithServiceProvider(
serviceProviderInterface = CreateTaskPreprocessor.class,
serviceProviders = TopLevelCreateTaskPreprocessor.class)
class ExampleClassWithServiceProviders {}
Map<Class<?>, List<Object>> extractServiceProviders =
extractServiceProviders(ExampleClassWithServiceProviders.class);
assertThat(extractServiceProviders).containsOnlyKeys(CreateTaskPreprocessor.class);
assertThat(extractServiceProviders.get(CreateTaskPreprocessor.class))
.extracting(Object::getClass)
.asList()
.containsExactly(TopLevelCreateTaskPreprocessor.class);
}
@Test
void should_InstantiateServiceProvider_When_ServiceProviderIsStaticMemberClass() {
@WithServiceProvider(
serviceProviderInterface = CreateTaskPreprocessor.class,
serviceProviders = StaticCreateTaskPreprocessor.class)
class ExampleClassWithServiceProviders {}
Map<Class<?>, List<Object>> extractServiceProviders =
extractServiceProviders(ExampleClassWithServiceProviders.class);
assertThat(extractServiceProviders).containsOnlyKeys(CreateTaskPreprocessor.class);
assertThat(extractServiceProviders.get(CreateTaskPreprocessor.class))
.extracting(Object::getClass)
.asList()
.containsExactly(StaticCreateTaskPreprocessor.class);
}
@Test
void should_InstantiateServiceProvider_When_ServiceProviderIsPrivateStaticMemberClass() {
@WithServiceProvider(
serviceProviderInterface = CreateTaskPreprocessor.class,
serviceProviders = PrivateStaticCreateTaskPreprocessor.class)
class ExampleClassWithServiceProviders {}
Map<Class<?>, List<Object>> extractServiceProviders =
extractServiceProviders(ExampleClassWithServiceProviders.class);
assertThat(extractServiceProviders).containsOnlyKeys(CreateTaskPreprocessor.class);
assertThat(extractServiceProviders.get(CreateTaskPreprocessor.class))
.extracting(Object::getClass)
.asList()
.containsExactly(PrivateStaticCreateTaskPreprocessor.class);
}
@Test
void should_InstantiateServiceProvider_When_ServiceProviderIsLocalClass() {
class LocalCreateTaskPreprocessor implements CreateTaskPreprocessor {
@Override
public void processTaskBeforeCreation(Task taskToProcess) {
// implementation not important for the tests
}
}
@WithServiceProvider(
serviceProviderInterface = CreateTaskPreprocessor.class,
serviceProviders = LocalCreateTaskPreprocessor.class)
class ExampleClassWithServiceProviders {}
Map<Class<?>, List<Object>> extractServiceProviders =
extractServiceProviders(ExampleClassWithServiceProviders.class);
assertThat(extractServiceProviders).containsOnlyKeys(CreateTaskPreprocessor.class);
assertThat(extractServiceProviders.get(CreateTaskPreprocessor.class))
.extracting(Object::getClass)
.asList()
.containsExactly(LocalCreateTaskPreprocessor.class);
}
@Test
void should_InstantiateServiceProvider_When_ServiceProviderIsNonStaticMemberClass() {
@WithServiceProvider(
serviceProviderInterface = CreateTaskPreprocessor.class,
serviceProviders = NonStaticCreateTaskPreprocessor.class)
class ExampleClassWithServiceProviders {}
Map<Class<?>, List<Object>> extractServiceProviders =
extractServiceProviders(ExampleClassWithServiceProviders.class);
assertThat(extractServiceProviders).containsOnlyKeys(CreateTaskPreprocessor.class);
assertThat(extractServiceProviders.get(CreateTaskPreprocessor.class))
.extracting(Object::getClass)
.asList()
.containsExactly(NonStaticCreateTaskPreprocessor.class);
}
@Test
void should_InstantiateServiceProvider_When_ServiceProviderIsPrivateNonStaticMemberClass() {
@WithServiceProvider(
serviceProviderInterface = CreateTaskPreprocessor.class,
serviceProviders = PrivateNonStaticCreateTaskPreprocessor.class)
class ExampleClassWithServiceProviders {}
Map<Class<?>, List<Object>> extractServiceProviders =
extractServiceProviders(ExampleClassWithServiceProviders.class);
assertThat(extractServiceProviders).containsOnlyKeys(CreateTaskPreprocessor.class);
assertThat(extractServiceProviders.get(CreateTaskPreprocessor.class))
.extracting(Object::getClass)
.asList()
.containsExactly(PrivateNonStaticCreateTaskPreprocessor.class);
}
class NonStaticCreateTaskPreprocessor implements CreateTaskPreprocessor {
@Override
public void processTaskBeforeCreation(Task taskToProcess) {
// implementation not important for the tests
}
}
class PrivateNonStaticCreateTaskPreprocessor implements CreateTaskPreprocessor {
@Override
public void processTaskBeforeCreation(Task taskToProcess) {
// implementation not important for the tests
}
}
}
@Nested
@TestInstance(Lifecycle.PER_CLASS)
class ExtractServiceProvidersFromSingleServiceProviderInterface {
@Test
void should_ExtractServiceProvider() {
@WithServiceProvider(
serviceProviderInterface = CreateTaskPreprocessor.class,
serviceProviders = DummyTaskPreprocessor1.class)
class ExampleClassWithServiceProviders {}
Map<Class<?>, List<Object>> extractServiceProviders =
extractServiceProviders(ExampleClassWithServiceProviders.class);
assertThat(extractServiceProviders).containsOnlyKeys(CreateTaskPreprocessor.class);
assertThat(extractServiceProviders.get(CreateTaskPreprocessor.class))
.extracting(Object::getClass)
.asList()
.containsExactly(DummyTaskPreprocessor1.class);
}
@Test
void should_ExtractMultipleServiceProviders_When_MultipleAreDefinedInOneAnnotation() {
@WithServiceProvider(
serviceProviderInterface = CreateTaskPreprocessor.class,
serviceProviders = {DummyTaskPreprocessor1.class, DummyTaskPreprocessor2.class})
class ExampleClassWithServiceProviders {}
Map<Class<?>, List<Object>> extractServiceProviders =
extractServiceProviders(ExampleClassWithServiceProviders.class);
assertThat(extractServiceProviders).containsOnlyKeys(CreateTaskPreprocessor.class);
assertThat(extractServiceProviders.get(CreateTaskPreprocessor.class))
.extracting(Object::getClass)
.asList()
.containsExactly(DummyTaskPreprocessor1.class, DummyTaskPreprocessor2.class);
}
@Test
void should_ExtractMultipleServiceProviders_When_MultipleAreDefinedInMultipleAnnotations() {
@WithServiceProvider(
serviceProviderInterface = CreateTaskPreprocessor.class,
serviceProviders = DummyTaskPreprocessor1.class)
@WithServiceProvider(
serviceProviderInterface = CreateTaskPreprocessor.class,
serviceProviders = DummyTaskPreprocessor2.class)
class ExampleClassWithServiceProviders {}
Map<Class<?>, List<Object>> extractServiceProviders =
extractServiceProviders(ExampleClassWithServiceProviders.class);
assertThat(extractServiceProviders).containsOnlyKeys(CreateTaskPreprocessor.class);
assertThat(extractServiceProviders.get(CreateTaskPreprocessor.class))
.extracting(Object::getClass)
.asList()
.containsExactly(DummyTaskPreprocessor1.class, DummyTaskPreprocessor2.class);
}
@Test
void should_ExtractSameServiceProviderMultipleTimes_When_ItIsDefinedMultipleTimes() {
@WithServiceProvider(
serviceProviderInterface = CreateTaskPreprocessor.class,
serviceProviders = {DummyTaskPreprocessor1.class, DummyTaskPreprocessor1.class})
class ExampleClassWithServiceProviders {}
Map<Class<?>, List<Object>> extractServiceProviders =
extractServiceProviders(ExampleClassWithServiceProviders.class);
assertThat(extractServiceProviders).containsOnlyKeys(CreateTaskPreprocessor.class);
assertThat(extractServiceProviders.get(CreateTaskPreprocessor.class))
.extracting(Object::getClass)
.asList()
.containsExactly(DummyTaskPreprocessor1.class, DummyTaskPreprocessor1.class);
}
}
@Nested
@TestInstance(Lifecycle.PER_CLASS)
class ExtractMultipleServiceProvidersFromMultipleServiceProviderInterfaces {
@Test
void should_ExtractServiceProviders() {
@WithServiceProvider(
serviceProviderInterface = CreateTaskPreprocessor.class,
serviceProviders = DummyTaskPreprocessor1.class)
@WithServiceProvider(
serviceProviderInterface = PriorityServiceProvider.class,
serviceProviders = DummyPriorityServiceProvider1.class)
class ExampleClassWithServiceProviders {}
Map<Class<?>, List<Object>> extractServiceProviders =
extractServiceProviders(ExampleClassWithServiceProviders.class);
assertThat(extractServiceProviders)
.containsOnlyKeys(CreateTaskPreprocessor.class, PriorityServiceProvider.class);
assertThat(extractServiceProviders.get(CreateTaskPreprocessor.class))
.extracting(Object::getClass)
.asList()
.containsExactly(DummyTaskPreprocessor1.class);
assertThat(extractServiceProviders.get(PriorityServiceProvider.class))
.extracting(Object::getClass)
.asList()
.containsExactly(DummyPriorityServiceProvider1.class);
}
@Test
void should_ExtractMultipleServiceProviders_When_MultipleAreDefinedInOneAnnotation() {
@WithServiceProvider(
serviceProviderInterface = CreateTaskPreprocessor.class,
serviceProviders = {DummyTaskPreprocessor1.class, DummyTaskPreprocessor2.class})
@WithServiceProvider(
serviceProviderInterface = PriorityServiceProvider.class,
serviceProviders = {
DummyPriorityServiceProvider1.class,
DummyPriorityServiceProvider2.class
})
class ExampleClassWithServiceProviders {}
Map<Class<?>, List<Object>> extractServiceProviders =
extractServiceProviders(ExampleClassWithServiceProviders.class);
assertThat(extractServiceProviders)
.containsOnlyKeys(CreateTaskPreprocessor.class, PriorityServiceProvider.class);
assertThat(extractServiceProviders.get(CreateTaskPreprocessor.class))
.extracting(Object::getClass)
.asList()
.containsExactly(DummyTaskPreprocessor1.class, DummyTaskPreprocessor2.class);
assertThat(extractServiceProviders.get(PriorityServiceProvider.class))
.extracting(Object::getClass)
.asList()
.containsExactly(
DummyPriorityServiceProvider1.class, DummyPriorityServiceProvider2.class);
}
}
@Nested
@TestInstance(Lifecycle.PER_CLASS)
class ErrorHandling {
@Test
void should_ThrowException_When_ServiceProviderInterfaceIsUnknown() {
@WithServiceProvider(
serviceProviderInterface = ErrorHandling.class,
serviceProviders = DummyTaskPreprocessor1.class)
class ExampleClassWithServiceProviders {}
ThrowingCallable call = () -> extractServiceProviders(ExampleClassWithServiceProviders.class);
assertThatThrownBy(call)
.isInstanceOf(JUnitException.class)
.hasMessage("SPI '%s' is not a TASKANA SPI.", ErrorHandling.class);
}
@Test
void should_ThrowException_When_ServiceProviderIsIncompatibleToServiceProviderInterface() {
@WithServiceProvider(
serviceProviderInterface = CreateTaskPreprocessor.class,
serviceProviders = DummyPriorityServiceProvider1.class)
class ExampleClassWithServiceProviders {}
ThrowingCallable call = () -> extractServiceProviders(ExampleClassWithServiceProviders.class);
assertThatThrownBy(call)
.isInstanceOf(JUnitException.class)
.hasMessage(
"At least one ServiceProvider does not implement the requested SPI '%s'",
CreateTaskPreprocessor.class);
}
@Test
void should_ThrowException_When_AnyServiceProviderIsIncompatibleToServiceProviderInterface() {
@WithServiceProvider(
serviceProviderInterface = CreateTaskPreprocessor.class,
serviceProviders = {DummyTaskPreprocessor1.class, DummyPriorityServiceProvider1.class})
class ExampleClassWithServiceProviders {}
ThrowingCallable call = () -> extractServiceProviders(ExampleClassWithServiceProviders.class);
assertThatThrownBy(call)
.isInstanceOf(JUnitException.class)
.hasMessage(
"At least one ServiceProvider does not implement the requested SPI '%s'",
CreateTaskPreprocessor.class);
}
}
}

View File

@ -0,0 +1,12 @@
package pro.taskana.testapi.util;
import pro.taskana.spi.task.api.CreateTaskPreprocessor;
import pro.taskana.task.api.models.Task;
public class TopLevelCreateTaskPreprocessor implements CreateTaskPreprocessor {
@Override
public void processTaskBeforeCreation(Task taskToProcess) {
// implementation not important for the tests
}
}