Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for nested tests annotated with JUnit 5's @Nested #11

Merged
merged 1 commit into from
Oct 22, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
package com.maciejwalkowiak.wiremock.spring;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.test.context.ContextConfigurationAttributes;
import org.springframework.test.context.ContextCustomizer;
import org.springframework.test.context.ContextCustomizerFactory;
import org.springframework.test.context.TestContextAnnotationUtils;

/**
* Creates {@link WireMockContextCustomizer} for test classes annotated with {@link EnableWireMock}.
Expand All @@ -16,12 +19,44 @@ public class WireMockContextCustomizerFactory implements ContextCustomizerFactor
@Override
public ContextCustomizer createContextCustomizer(Class<?> testClass,
List<ContextConfigurationAttributes> configAttributes) {
EnableWireMock annotation = AnnotationUtils.findAnnotation(testClass, EnableWireMock.class);
// scan class and all enclosing classes if the test class is @Nested
ConfigureWiremockHolder holder = new ConfigureWiremockHolder();
parseDefinitions(testClass, holder);

if (annotation != null) {
return new WireMockContextCustomizer(annotation.value());
} else {
if (holder.isEmpty()) {
return null;
} else {
return new WireMockContextCustomizer(holder.asArray());
}
}

private void parseDefinitions(Class<?> testClass, ConfigureWiremockHolder parser) {
parser.parse(testClass);
if (TestContextAnnotationUtils.searchEnclosingClass(testClass)) {
parseDefinitions(testClass.getEnclosingClass(), parser);
}
}

private static class ConfigureWiremockHolder {
private final List<ConfigureWireMock> annotations = new ArrayList<>();

void add(ConfigureWireMock[] annotations) {
this.annotations.addAll(Arrays.asList(annotations));
}

void parse(Class<?> clazz) {
EnableWireMock annotation = AnnotationUtils.findAnnotation(clazz, EnableWireMock.class);
if (annotation != null) {
add(annotation.value());
}
}

boolean isEmpty() {
return annotations.isEmpty();
}

ConfigureWireMock[] asArray() {
return annotations.toArray(new ConfigureWireMock[]{});
}
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package com.maciejwalkowiak.wiremock.spring;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.List;
import java.util.function.Function;

import com.github.tomakehurst.wiremock.WireMockServer;
import org.junit.jupiter.api.extension.BeforeEachCallback;
Expand All @@ -24,29 +26,21 @@ public void beforeEach(ExtensionContext extensionContext) throws Exception {
Store.INSTANCE.findAllInstances(extensionContext).forEach(WireMockServer::resetAll);

// inject properties into test class fields
injectWithLegacyWireMockAnnotation(extensionContext);
injectWireMockInstances(extensionContext);
injectWireMockInstances(extensionContext, WireMock.class, WireMock::value);
injectWireMockInstances(extensionContext, InjectWireMock.class, InjectWireMock::value);
}

private static void injectWithLegacyWireMockAnnotation(ExtensionContext extensionContext) throws IllegalAccessException {
List<Field> annotatedFields = AnnotationSupport.findAnnotatedFields(extensionContext.getRequiredTestClass(), WireMock.class);
for (Field annotatedField : annotatedFields) {
WireMock annotation = annotatedField.getAnnotation(WireMock.class);
annotatedField.setAccessible(true);

WireMockServer wiremock = Store.INSTANCE.findRequiredWireMockInstance(extensionContext, annotation.value());
annotatedField.set(extensionContext.getRequiredTestInstance(), wiremock);
}
}

private static void injectWireMockInstances(ExtensionContext extensionContext) throws IllegalAccessException {
List<Field> annotatedFields = AnnotationSupport.findAnnotatedFields(extensionContext.getRequiredTestClass(), InjectWireMock.class);
for (Field annotatedField : annotatedFields) {
InjectWireMock annotation = annotatedField.getAnnotation(InjectWireMock.class);
annotatedField.setAccessible(true);

WireMockServer wiremock = Store.INSTANCE.findRequiredWireMockInstance(extensionContext, annotation.value());
annotatedField.set(extensionContext.getRequiredTestInstance(), wiremock);
private static <T extends Annotation> void injectWireMockInstances(ExtensionContext extensionContext, Class<T> annotation, Function<T, String> fn) throws IllegalAccessException {
// getRequiredTestInstances() return multiple instances for nested tests
for (Object testInstance : extensionContext.getRequiredTestInstances().getAllInstances()) {
List<Field> annotatedFields = AnnotationSupport.findAnnotatedFields(testInstance.getClass(), annotation);
for (Field annotatedField : annotatedFields) {
T annotationValue = annotatedField.getAnnotation(annotation);
annotatedField.setAccessible(true);

WireMockServer wiremock = Store.INSTANCE.findRequiredWireMockInstance(extensionContext, fn.apply(annotationValue));
annotatedField.set(testInstance, wiremock);
}
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package app;

import com.github.tomakehurst.wiremock.WireMockServer;
import com.maciejwalkowiak.wiremock.spring.ConfigureWireMock;
import com.maciejwalkowiak.wiremock.spring.EnableWireMock;
import com.maciejwalkowiak.wiremock.spring.InjectWireMock;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.core.env.Environment;

import static org.assertj.core.api.Assertions.assertThat;

@SpringBootTest(classes = NestedClassWireMockSpringExtensionTest.AppConfiguration.class)
@EnableWireMock({
@ConfigureWireMock(name = "user-service", property = "user-service.url"),
@ConfigureWireMock(name = "todo-service", property = "todo-service.url"),
@ConfigureWireMock(name = "noproperty-service")
})
public class NestedClassWireMockSpringExtensionTest {

@SpringBootApplication
static class AppConfiguration {
}

@Autowired
private Environment environment;

@InjectWireMock("todo-service")
private WireMockServer topLevelClassTodoService;

@Nested
@DisplayName("Test Something")
class NestedTest {

@InjectWireMock("todo-service")
private WireMockServer nestedClassTodoService;

@Test
void injectsWiremockServerToMethodParameter(@InjectWireMock("user-service") WireMockServer wireMockServer) {
assertWireMockServer(wireMockServer, "user-service.url");
}

@Test
void injectsWiremockServerToNestedClassField() {
assertWireMockServer(nestedClassTodoService, "todo-service.url");
}

@Test
void injectsWiremockServerToTopLevelClassField() {
assertWireMockServer(topLevelClassTodoService, "todo-service.url");
}

@Test
void doesNotSetPropertyWhenNotProvided(@InjectWireMock("noproperty-service") WireMockServer wireMockServer) {
assertThat(wireMockServer)
.as("creates WireMock instance")
.isNotNull();
}

private void assertWireMockServer(WireMockServer wireMockServer, String property) {
assertThat(wireMockServer)
.as("creates WireMock instance")
.isNotNull();
assertThat(wireMockServer.baseUrl())
.as("WireMock baseUrl is set")
.isNotNull();
assertThat(wireMockServer.port())
.as("sets random port")
.isNotZero();
assertThat(environment.getProperty(property))
.as("sets Spring property")
.isEqualTo(wireMockServer.baseUrl());
}
}
}
Loading