
io.dropwizard.testing.DropwizardTestSupport Maven / Gradle / Ivy
package io.dropwizard.testing;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import io.dropwizard.Application;
import io.dropwizard.Configuration;
import io.dropwizard.cli.ServerCommand;
import io.dropwizard.configuration.ConfigurationFactory;
import io.dropwizard.lifecycle.Managed;
import io.dropwizard.setup.Bootstrap;
import io.dropwizard.setup.Environment;
import net.sourceforge.argparse4j.inf.Namespace;
import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import static com.google.common.base.MoreObjects.firstNonNull;
import static com.google.common.base.Throwables.propagate;
/**
* A test support class for starting and stopping your application at the start and end of a test class.
*
* By default, the {@link Application} will be constructed using reflection to invoke the nullary
* constructor. If your application does not provide a public nullary constructor, you will need to
* override the {@link #newApplication()} method to provide your application instance(s).
*
*
* @param the configuration type
*/
public class DropwizardTestSupport {
protected final Class extends Application> applicationClass;
protected final String configPath;
protected final Set configOverrides;
protected final Optional customPropertyPrefix;
/**
* Flag that indicates whether instance was constructed with an explicit
* Configuration object or not; handling of the two cases differ.
* Needed because state of {@link #configuration} changes during lifecycle.
*/
protected final boolean explicitConfig;
protected C configuration;
protected Application application;
protected Environment environment;
protected Server jettyServer;
protected List> listeners = new ArrayList<>();
public DropwizardTestSupport(Class extends Application> applicationClass,
@Nullable String configPath,
ConfigOverride... configOverrides) {
this(applicationClass, configPath, Optional.empty(), configOverrides);
}
public DropwizardTestSupport(Class extends Application> applicationClass, String configPath,
Optional customPropertyPrefix, ConfigOverride... configOverrides) {
this.applicationClass = applicationClass;
this.configPath = configPath;
this.configOverrides = ImmutableSet.copyOf(firstNonNull(configOverrides, new ConfigOverride[0]));
this.customPropertyPrefix = customPropertyPrefix;
explicitConfig = false;
}
/**
* Alternative constructor that may be used to directly provide Configuration
* to use, instead of specifying resource path for locating data to create
* Configuration.
*
* @since 0.9
*
* @param applicationClass Type of Application to create
* @param configuration Pre-constructed configuration object caller provides; will not
* be manipulated in any way, no overriding
*/
public DropwizardTestSupport(Class extends Application> applicationClass,
C configuration) {
if (configuration == null) {
throw new IllegalArgumentException("Can not pass null configuration for explicitly configured instance");
}
this.applicationClass = applicationClass;
configPath = "";
configOverrides = ImmutableSet.of();
customPropertyPrefix = Optional.empty();
this.configuration = configuration;
explicitConfig = true;
}
public DropwizardTestSupport addListener(ServiceListener listener) {
this.listeners.add(listener);
return this;
}
public DropwizardTestSupport manage(final Managed managed) {
return addListener(new ServiceListener() {
@Override
public void onRun(C configuration, Environment environment, DropwizardTestSupport rule) throws Exception {
environment.lifecycle().manage(managed);
}
});
}
public void before() {
applyConfigOverrides();
startIfRequired();
}
public void after() {
try {
stopIfRequired();
} finally {
resetConfigOverrides();
}
}
private void stopIfRequired() {
if (jettyServer != null) {
for (ServiceListener listener : listeners) {
try {
listener.onStop(this);
} catch (Exception ignored) {
}
}
try {
jettyServer.stop();
} catch (Exception e) {
throw propagate(e);
} finally {
jettyServer = null;
}
}
}
private void applyConfigOverrides() {
for (ConfigOverride configOverride : configOverrides) {
configOverride.addToSystemProperties();
}
}
private void resetConfigOverrides() {
for (ConfigOverride configOverride : configOverrides) {
configOverride.removeFromSystemProperties();
}
}
private void startIfRequired() {
if (jettyServer != null) {
return;
}
try {
application = newApplication();
final Bootstrap bootstrap = new Bootstrap(application) {
@Override
public void run(C configuration, Environment environment) throws Exception {
environment.lifecycle().addServerLifecycleListener(server -> jettyServer = server);
DropwizardTestSupport.this.configuration = configuration;
DropwizardTestSupport.this.environment = environment;
super.run(configuration, environment);
for (ServiceListener listener : listeners) {
try {
listener.onRun(configuration, environment, DropwizardTestSupport.this);
} catch (Exception ex) {
throw new RuntimeException("Error running app rule start listener", ex);
}
}
}
};
if (explicitConfig) {
bootstrap.setConfigurationFactoryFactory((klass, validator, objectMapper, propertyPrefix) ->
new POJOConfigurationFactory<>(configuration));
} else if (customPropertyPrefix.isPresent()) {
bootstrap.setConfigurationFactoryFactory((klass, validator, objectMapper, propertyPrefix) ->
new ConfigurationFactory<>(klass, validator, objectMapper, customPropertyPrefix.get()));
}
application.initialize(bootstrap);
final ServerCommand command = new ServerCommand<>(application);
final ImmutableMap.Builder file = ImmutableMap.builder();
if (!Strings.isNullOrEmpty(configPath)) {
file.put("file", configPath);
}
final Namespace namespace = new Namespace(file.build());
command.run(bootstrap, namespace);
} catch (Exception e) {
throw propagate(e);
}
}
public C getConfiguration() {
return configuration;
}
public int getLocalPort() {
return ((ServerConnector) jettyServer.getConnectors()[0]).getLocalPort();
}
public int getAdminPort() {
final Connector[] connectors = jettyServer.getConnectors();
return ((ServerConnector) connectors[connectors.length -1]).getLocalPort();
}
public int getPort(int connectorIndex) {
return ((ServerConnector) jettyServer.getConnectors()[connectorIndex]).getLocalPort();
}
public Application newApplication() {
try {
return applicationClass.newInstance();
} catch (Exception e) {
throw propagate(e);
}
}
@SuppressWarnings("unchecked")
public > A getApplication() {
return (A) application;
}
public Environment getEnvironment() {
return environment;
}
public ObjectMapper getObjectMapper() {
return getEnvironment().getObjectMapper();
}
public abstract static class ServiceListener {
public void onRun(T configuration, Environment environment, DropwizardTestSupport rule) throws Exception {
// Default NOP
}
public void onStop(DropwizardTestSupport rule) throws Exception {
// Default NOP
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy