Skip to content

Commit

Permalink
fix: wiremock default port was not always configured
Browse files Browse the repository at this point in the history
Cannot rely on ContextCustomizer always being invoked because of caching.

Now configuring Wiremock port in Junit extension.
  • Loading branch information
tomasbjerre committed Oct 11, 2024
1 parent 99c2fde commit 14b3f3f
Show file tree
Hide file tree
Showing 7 changed files with 163 additions and 20 deletions.
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ allprojects {
testImplementation platform('org.junit:junit-bom:5.11.2')
testImplementation 'org.junit.jupiter:junit-jupiter'
testImplementation 'org.junit.platform:junit-platform-launcher'
testImplementation 'io.rest-assured:rest-assured:5.5.0'

constraints {
implementation('org.apache.commons:commons-compress:1.26.0') {
Expand Down Expand Up @@ -58,6 +59,5 @@ project('wiremock-spring-boot-example', {
implementation "org.springframework.boot:spring-boot-starter-webflux:3.3.4"

testImplementation rootProject
testImplementation 'io.rest-assured:rest-assured:5.5.0'
}
})
2 changes: 1 addition & 1 deletion src/main/java/org/wiremock/spring/EnableWireMock.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
*/
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@ExtendWith(org.wiremock.spring.internal.WireMockSpringExtension.class)
@ExtendWith(org.wiremock.spring.internal.WireMockSpringJunitExtension.class)
public @interface EnableWireMock {
/**
* A list of {@link com.github.tomakehurst.wiremock.WireMockServer} configurations. For each
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package org.wiremock.spring.internal;

import com.github.tomakehurst.wiremock.WireMockServer;
import com.github.tomakehurst.wiremock.client.WireMock;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
Expand Down Expand Up @@ -45,11 +44,7 @@ public WireMockContextCustomizer(final ConfigureWireMock... configurations) {
public void customizeContext(
final ConfigurableApplicationContext context, final MergedContextConfiguration mergedConfig) {
for (final ConfigureWireMock configureWiremock : this.configuration) {
final WireMockServer wireMockServer =
this.resolveOrCreateWireMockServer(context, configureWiremock);
if (this.configuration.size() == 1) {
WireMock.configureFor(wireMockServer.port());
}
this.resolveOrCreateWireMockServer(context, configureWiremock);
}
}

Expand All @@ -67,6 +62,11 @@ private WireMockServer resolveOrCreateWireMockServer(
return wireMockServer;
}

/**
* The docs in {@link ContextCustomizer} states that equals and hashcode is being used for caching
* and needs implementation. The customizeContext method will not be invoked for all tests,
* because of caching.
*/
@Override
public boolean equals(final Object o) {
if (this == o) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,19 @@
* @author Maciej Walkowiak
*/
public class WireMockContextCustomizerFactory implements ContextCustomizerFactory {
private static final ConfigureWireMock DEFAULT_CONFIGURE_WIREMOCK =
static final ConfigureWireMock DEFAULT_CONFIGURE_WIREMOCK =
DefaultConfigureWireMock.class.getAnnotation(ConfigureWireMock.class);

@ConfigureWireMock(name = "wiremock")
private static class DefaultConfigureWireMock {}

static ConfigureWireMock[] getConfigureWireMocksOrDefault(final ConfigureWireMock... value) {
if (value == null || value.length == 0) {
return new ConfigureWireMock[] {WireMockContextCustomizerFactory.DEFAULT_CONFIGURE_WIREMOCK};
}
return value;
}

@Override
public ContextCustomizer createContextCustomizer(
final Class<?> testClass, final List<ContextConfigurationAttributes> configAttributes) {
Expand Down Expand Up @@ -54,12 +61,7 @@ void add(final ConfigureWireMock... annotations) {
void parse(final Class<?> clazz) {
final EnableWireMock annotation = AnnotationUtils.findAnnotation(clazz, EnableWireMock.class);
if (annotation != null) {
final ConfigureWireMock[] value = annotation.value();
if (value.length == 0) {
this.add(WireMockContextCustomizerFactory.DEFAULT_CONFIGURE_WIREMOCK);
} else {
this.add(value);
}
this.add(getConfigureWireMocksOrDefault(annotation.value()));
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@
import org.junit.jupiter.api.extension.ParameterContext;
import org.junit.jupiter.api.extension.ParameterResolver;
import org.junit.platform.commons.support.AnnotationSupport;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.AnnotationUtils;
import org.wiremock.spring.ConfigureWireMock;
import org.wiremock.spring.EnableWireMock;
import org.wiremock.spring.InjectWireMock;

/**
Expand All @@ -20,8 +25,9 @@
*
* @author Maciej Walkowiak
*/
public class WireMockSpringExtension
public class WireMockSpringJunitExtension
implements BeforeEachCallback, AfterEachCallback, ParameterResolver {
private static final Logger LOGGER = LoggerFactory.getLogger(WireMockSpringJunitExtension.class);

@Override
public void beforeEach(final ExtensionContext extensionContext) throws Exception {
Expand All @@ -30,6 +36,45 @@ public void beforeEach(final ExtensionContext extensionContext) throws Exception

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

this.configureWireMockForDefaultInstance(extensionContext);
}

private void configureWireMockForDefaultInstance(final ExtensionContext extensionContext) {
final List<Object> instances = extensionContext.getRequiredTestInstances().getAllInstances();
WireMockServer wiremock = null;
String wireMockName = null;
for (final Object instance : instances) {
final EnableWireMock enableWireMockAnnotation =
AnnotationUtils.findAnnotation(instance.getClass(), EnableWireMock.class);
if (enableWireMockAnnotation == null) {
continue;
}
final ConfigureWireMock[] wireMockServers =
WireMockContextCustomizerFactory.getConfigureWireMocksOrDefault(
enableWireMockAnnotation.value());
if (wireMockServers.length > 1) {
LOGGER.info(
"Not configuring WireMock for default instance when several ConfigureWireMock ("
+ wireMockServers.length
+ ")");
}
if (wiremock != null) {
LOGGER.info("Not configuring WireMock for default instance when several candidates found");
return;
}
wireMockName = wireMockServers[0].name();
wiremock = Store.INSTANCE.findRequiredWireMockInstance(extensionContext, wireMockName);
}
if (wiremock != null) {
LOGGER.info(
"Configuring WireMock for default instance, '"
+ wireMockName
+ "' on '"
+ wiremock.port()
+ "'.");
WireMock.configureFor(wiremock.port());
}
}

@Override
Expand Down
81 changes: 81 additions & 0 deletions src/test/java/app/NestedClassSingleWireMockTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package app;

import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
import static com.github.tomakehurst.wiremock.client.WireMock.get;
import static org.assertj.core.api.Assertions.assertThat;

import com.github.tomakehurst.wiremock.WireMockServer;
import com.github.tomakehurst.wiremock.client.WireMock;
import io.restassured.RestAssured;
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 org.wiremock.spring.ConfigureWireMock;
import org.wiremock.spring.EnableWireMock;
import org.wiremock.spring.InjectWireMock;

@SpringBootTest(classes = NestedClassSingleWireMockTest.AppConfiguration.class)
@EnableWireMock({
@ConfigureWireMock(
name = "todo-service",
baseUrlProperties = "todo-service.url",
portProperties = "todo-service.port")
})
public class NestedClassSingleWireMockTest {

@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 injectsWiremockServerToNestedClassField() {
this.assertWireMockServer(
this.nestedClassTodoService, "todo-service.url", "todo-service.port");
}

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

private void assertWireMockServer(
final WireMockServer wireMockServer, final String property, final String portProperty) {
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(
Integer.valueOf(
NestedClassSingleWireMockTest.this.environment.getProperty(portProperty)))
.as("sets Spring port property")
.isEqualTo(wireMockServer.port());
assertThat(NestedClassSingleWireMockTest.this.environment.getProperty(property))
.as("sets Spring property")
.isEqualTo(wireMockServer.baseUrl());

// Test that WireMock is configured for the correct WireMock instance
// Suffixed with port to make it differ for different test runs and servers
final String mockedPath = "/the_default_prop_mock-" + wireMockServer.port();
WireMock.stubFor(get(mockedPath).willReturn(aResponse().withStatus(202)));
RestAssured.baseURI = wireMockServer.baseUrl();
RestAssured.when().get(mockedPath).then().statusCode(202);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,17 @@

import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
import static com.github.tomakehurst.wiremock.client.WireMock.get;
import static org.assertj.core.api.Assertions.assertThat;

import com.github.tomakehurst.wiremock.WireMockServer;
import com.github.tomakehurst.wiremock.client.WireMock;
import io.restassured.RestAssured;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.test.context.SpringBootTest;
import org.wiremock.spring.EnableWireMock;
import org.wiremock.spring.InjectWireMock;

@SpringBootTest
@EnableWireMock
Expand All @@ -21,14 +24,26 @@ class DefaultPropertiesTest {
@Value("${wiremock.server.port}")
private String wiremockPort;

@InjectWireMock WireMockServer wireMockServer;

@BeforeEach
public void before() {
WireMock.stubFor(get("/the_default_prop_mock").willReturn(aResponse().withStatus(202)));
}
public void before() {}

@Test
void test() {
void testCanInvoke() {
WireMock.stubFor(get("/the_default_prop_mock").willReturn(aResponse().withStatus(202)));

RestAssured.baseURI = this.wiremockUrl;
RestAssured.when().get("/the_default_prop_mock").then().statusCode(202);
}

@Test
void testUrlNotNull() {
assertThat(this.wiremockUrl).isNotNull();
}

@Test
void testPortNotNull() {
assertThat(this.wiremockPort).isNotNull();
}
}

0 comments on commit 14b3f3f

Please sign in to comment.