GH-466 - Polishing.

Moved integration test to integration tests module.
This commit is contained in:
Oliver Drotbohm
2024-07-17 00:17:43 +02:00
parent bf120cf44b
commit 3b853507c1
3 changed files with 54 additions and 33 deletions

View File

@@ -15,23 +15,29 @@
*/
package otherpackage.integration;
import example.order.Order;
import example.order.OrderManagement;
import lombok.RequiredArgsConstructor;
import static org.assertj.core.api.Assertions.*;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.modulith.test.ApplicationModuleTest;
import com.acme.myproject.moduleA.ServiceComponentA;
/**
* Test to showcase {@link ApplicationModuleTest} outside of a module package
*
* @author Lukas Dohmen
*/
@ApplicationModuleTest(module = "order", classes = {example.Application.class})
@RequiredArgsConstructor
class OrderIntegrationTests {
@ApplicationModuleTest(
module = "moduleA",
classes = { com.acme.myproject.Application.class },
verifyAutomatically = false)
class TestOutsideModulePackageIntegrationTests {
private final OrderManagement orders;
@Autowired ServiceComponentA componentA;
@Test
void completesOrder() {
orders.complete(new Order());
void injectsModuleComponent() {
assertThat(componentA).isNotNull();
}
}

View File

@@ -77,10 +77,22 @@ public @interface ApplicationModuleTest {
String[] extraIncludes() default {};
/**
* Logical name of the module in case {@link ApplicationModuleTest} will be used outside a module package
* Logical name of the module to be bootstrapped in case {@link ApplicationModuleTest} will be used outside a module
* package
*
* @return will never be {@literal null}.
* @since 1.3
*/
String module() default "";
/**
* Alias for {@link SpringBootTest#classes()}. Useful to define the main application class in case the test is located
* outside a module package and that class doesn't reside in any of the parent packages.
*
* @return will never be {@literal null}.
* @see #module()
* @since 1.3
*/
@AliasFor(annotation = SpringBootTest.class)
Class<?>[] classes() default {};

View File

@@ -23,6 +23,7 @@ import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.slf4j.Logger;
@@ -34,11 +35,13 @@ import org.springframework.modulith.core.ApplicationModule;
import org.springframework.modulith.core.ApplicationModules;
import org.springframework.modulith.core.JavaPackage;
import org.springframework.modulith.test.ApplicationModuleTest.BootstrapMode;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import org.springframework.util.function.SingletonSupplier;
/**
* @author Oliver Drotbohm
* @author Lukas Dohmen
*/
public class ModuleTestExecution implements Iterable<ApplicationModule> {
@@ -95,21 +98,14 @@ public class ModuleTestExecution implements Iterable<ApplicationModule> {
var annotation = AnnotatedElementUtils.findMergedAnnotation(type, ApplicationModuleTest.class);
var packageName = type.getPackage().getName();
var optionalModulithType = findSpringBootApplicationByClasses(annotation);
var modulithType = optionalModulithType.orElseGet(() -> MODULITH_TYPES.computeIfAbsent(type,
it -> new AnnotatedClassFinder(SpringBootApplication.class).findFromPackage(packageName)));
var modules = ApplicationModules.of(modulithType);
var modules = ApplicationModules.of(findSpringBootApplicationByClasses(annotation, type));
var moduleName = annotation.module();
ApplicationModule module;
if (StringUtils.hasText(moduleName)) {
module = modules.getModuleByName(moduleName).orElseThrow( //
() -> new IllegalStateException(String.format("Unable to find module %s!", moduleName)));
} else {
module = modules.getModuleForPackage(packageName).orElseThrow( //
() -> new IllegalStateException(String.format("Package %s is not part of any module!", packageName)));
}
var module = StringUtils.hasText(moduleName)
? modules.getModuleByName(moduleName).orElseThrow( //
() -> new IllegalStateException(String.format("Unable to find module %s!", moduleName)))
: modules.getModuleForPackage(packageName).orElseThrow( //
() -> new IllegalStateException(String.format("Package %s is not part of any module!", packageName)));
return EXECUTIONS.computeIfAbsent(new Key(module.getBasePackage().getName(), annotation),
it -> new ModuleTestExecution(annotation, modules, module));
@@ -240,15 +236,22 @@ public class ModuleTestExecution implements Iterable<ApplicationModule> {
.flatMap(Optional::stream);
}
private static Optional<Class<?>> findSpringBootApplicationByClasses(ApplicationModuleTest annotation) {
for (Class<?> clazz : annotation.classes()) {
Class<?> modulithType = MODULITH_TYPES.computeIfAbsent(clazz,
it -> new AnnotatedClassFinder(SpringBootApplication.class).findFromPackage(clazz.getPackageName()));
if (modulithType != null) {
return Optional.of(modulithType);
}
}
return Optional.empty();
private static Class<?> findSpringBootApplicationByClasses(ApplicationModuleTest annotation,
Class<?> testClass) {
var types = ObjectUtils.addObjectToArray(annotation.classes(), testClass);
return Arrays.stream(types)
.<Class<?>> map(ModuleTestExecution::lookupSpringBootApplicationAnnotation)
.findFirst()
.orElseThrow(() -> new IllegalStateException("Couldn't find @SpringBootApplication traversing %s."
.formatted(Arrays.stream(types).map(Class::getName).collect(Collectors.joining(", ")))));
}
private static Class<?> lookupSpringBootApplicationAnnotation(Class<?> clazz) {
return MODULITH_TYPES.computeIfAbsent(clazz,
it -> new AnnotatedClassFinder(SpringBootApplication.class).findFromClass(clazz));
}
private static record Key(String moduleBasePackage, ApplicationModuleTest annotation) {}