io.knotx.junit5.wiremock.KnotxWiremockExtension Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of knotx-junit5 Show documentation
Show all versions of knotx-junit5 Show documentation
Testing Knot.x with JUnit 5
/*
* Copyright (C) 2018 Knot.x Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.knotx.junit5.wiremock;
import static io.knotx.junit5.util.ReflectUtil.forEachWiremockFields;
import static io.knotx.junit5.util.StreamUtil.anyKeyStartsWith;
import com.github.tomakehurst.wiremock.WireMockServer;
import com.github.tomakehurst.wiremock.client.MappingBuilder;
import com.github.tomakehurst.wiremock.client.WireMock;
import com.github.tomakehurst.wiremock.core.Options;
import com.github.tomakehurst.wiremock.core.WireMockConfiguration;
import com.github.tomakehurst.wiremock.stubbing.StubMapping;
import com.google.common.collect.ImmutableMap;
import com.typesafe.config.Config;
import io.knotx.junit5.KnotxBaseExtension;
import io.knotx.junit5.KnotxExtension;
import io.knotx.junit5.util.HoconUtil;
import io.knotx.junit5.util.ReflectUtil;
import io.knotx.junit5.util.StreamUtil;
import io.vertx.core.json.JsonObject;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.apache.commons.lang3.StringUtils;
import org.junit.jupiter.api.extension.AfterAllCallback;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.api.extension.ParameterContext;
import org.junit.jupiter.api.extension.ParameterResolutionException;
import org.junit.jupiter.api.extension.ParameterResolver;
import org.junit.jupiter.api.extension.TestInstancePostProcessor;
/**
* Manages {@linkplain WireMockServer} instances for parameter and field injection. Standalone
* extension, can be used separately from {@linkplain KnotxExtension}.
*/
public class KnotxWiremockExtension extends KnotxBaseExtension
implements ParameterResolver, TestInstancePostProcessor, AfterAllCallback {
private static final String WIREMOCK_NAMESPACE = "test.wiremock";
private static final ReentrantLock globalMapsLock = new ReentrantLock(true);
private static final HashMap portToServerMap = new HashMap<>();
private static final HashMap serviceNameToServerMap =
new HashMap<>();
private final HashMap localInstanceServers = new HashMap<>();
/**
* Retrieve Wiremock for given port and add given mappings
*
* @param port on which server is configured
* @param mappingBuilder given mapping
* @return created mapping
* @see WireMock#stubFor(MappingBuilder)
*/
public static StubMapping stubForPort(int port, MappingBuilder mappingBuilder) {
return getOrCreateWiremock(port).register(mappingBuilder);
}
/**
* Add given mappings for this server
*
* @param server to which add mapping
* @param mappingBuilder given mapping
* @return created mapping
* @see WireMock#stubFor(MappingBuilder)
*/
public static StubMapping stubForServer(WireMockServer server, MappingBuilder mappingBuilder) {
return stubForPort(server.port(), mappingBuilder);
}
@Override
public boolean supportsParameter(
ParameterContext parameterContext, ExtensionContext extensionContext)
throws ParameterResolutionException {
Class> type = getType(parameterContext);
if (parameterContext.getParameter().isAnnotationPresent(ClasspathResourcesMockServer.class)) {
if (type.equals(WireMockServer.class) || type.equals(Integer.class)) {
return true;
}
if (type.equals(String.class)) {
throw new ParameterResolutionException(
"Annotating String with ClasspathResourcesMockServer is not supported");
}
}
return false;
}
@Override
public Object resolveParameter(
ParameterContext parameterContext, ExtensionContext extensionContext)
throws ParameterResolutionException {
return parameterContext
.findAnnotation(ClasspathResourcesMockServer.class)
.map(
knotxWiremock -> {
String nameReference =
getFullyQualifiedName(extensionContext, parameterContext, knotxWiremock);
WireMockServer server = setupWiremockServer(nameReference, knotxWiremock);
Class> type = getType(parameterContext);
if (type.equals(WireMockServer.class)) {
return server;
} else if (type.equals(Integer.class)) {
return server.port();
}
throw new IllegalStateException("This should never happen!");
})
.orElseThrow(
() ->
new IllegalStateException(
"Not supported parameter: " + parameterContext.getParameter().getName()));
}
private String getFullyQualifiedName(
ExtensionContext extensionContext,
ParameterContext parameterContext,
ClasspathResourcesMockServer classpathResourcesMockServer) {
String paramName = checkAndGetParameterName(parameterContext);
if (classpathResourcesMockServer.port() == Options.DYNAMIC_PORT) {
String fieldName = getClassFieldName(extensionContext, paramName);
if (localInstanceServers.containsKey(fieldName)) {
return fieldName;
}
}
return getClassMethodParameterName(extensionContext, parameterContext);
}
private String checkAndGetParameterName(ParameterContext parameterContext) {
String name = parameterContext.getParameter().getName();
if (name.startsWith("arg")) {
throw new IllegalStateException(
"Please configure 'options.compilerArgs << \"-parameters\"', please check the README file.");
}
return name;
}
@Override
public void afterAll(ExtensionContext context) {
globalMapsLock.lock();
// cleanup our local instances, operate only on port numbers as they're unique
localInstanceServers.forEach(
(service, server) -> {
if (server.isRunning()) {
server.shutdown();
}
// remove instances from global map
int port = server.getMockConfig().port;
portToServerMap.remove(port);
serviceNameToServerMap.values().removeIf(s -> s.getMockConfig().port == port);
});
globalMapsLock.unlock();
}
/** Sets up all annotated fields in test class */
@Override
public void postProcessTestInstance(Object testInstance, ExtensionContext context) {
Optional> testClass = context.getTestClass();
if (!testClass.isPresent()) {
return;
}
forEachWiremockFields(
testClass.get(),
field -> {
String reference = getClassFieldName(context, field);
ClasspathResourcesMockServer wiremockAnnotation =
field.getAnnotation(ClasspathResourcesMockServer.class);
WireMockServer server = setupWiremockServer(reference, wiremockAnnotation);
ReflectUtil.setField(testInstance, field, server);
});
}
public void addMissingInstanceServers(String forClass, ExtensionContext context) {
Optional
© 2015 - 2024 Weber Informatics LLC | Privacy Policy