Skip to content

Commit

Permalink
Improve Dropwizard test support (#2673)
Browse files Browse the repository at this point in the history
* Allow specifying ConfigurationSourceProvider in DropwizardTestSupport, DropwizardAppRule, and DropwizardAppExtension
* Mark constructors with `Optional<String>` for configuration file as deprecated in favor of nullable versions
  * See https://rules.sonarsource.com/java/RSPEC-3553
* Better exceptions when initializing the test application fails
* Clean up tests and remove redundant test applications

Closes #1709
Closes #1830
  • Loading branch information
joschi committed Mar 3, 2019
1 parent 179a920 commit 86abeb0
Show file tree
Hide file tree
Showing 17 changed files with 440 additions and 163 deletions.
Expand Up @@ -155,7 +155,7 @@ public Response toResponse(ConstraintViolationException e) {
private DropwizardTestSupport<?> dropwizardTestSupport = mock(DropwizardTestSupport.class);
private Client client = new JerseyClientBuilder().build();

public void setup(Class<? extends Application<TestConfiguration>> applicationClass) {
public void setup(Class<? extends Application<TestConfiguration>> applicationClass) throws Exception {
dropwizardTestSupport = new DropwizardTestSupport<>(applicationClass, ResourceHelpers.resourceFilePath("hibernate-integration-test.yaml"),
ConfigOverride.config("dataSource.url", "jdbc:hsqldb:mem:DbTest" + System.nanoTime() + "?hsqldb.translate_dti_types=false"));
dropwizardTestSupport.before();
Expand Down
Expand Up @@ -5,6 +5,8 @@
import io.dropwizard.Configuration;
import io.dropwizard.cli.Command;
import io.dropwizard.cli.ServerCommand;
import io.dropwizard.configuration.ConfigurationSourceProvider;
import io.dropwizard.configuration.FileConfigurationSourceProvider;
import io.dropwizard.configuration.YamlConfigurationFactory;
import io.dropwizard.lifecycle.Managed;
import io.dropwizard.setup.Bootstrap;
Expand All @@ -17,6 +19,7 @@
import org.eclipse.jetty.server.ServerConnector;

import javax.annotation.Nullable;
import javax.validation.constraints.NotNull;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Collections;
Expand All @@ -43,8 +46,10 @@ public class DropwizardTestSupport<C extends Configuration> {

@Nullable
protected final String configPath;
protected final ConfigurationSourceProvider configSourceProvider;
protected final Set<ConfigOverride> configOverrides;
protected final Optional<String> customPropertyPrefix;
@Nullable
protected final String customPropertyPrefix;
protected final Function<Application<C>, Command> commandInstantiator;

/**
Expand All @@ -68,25 +73,76 @@ public class DropwizardTestSupport<C extends Configuration> {
protected List<ServiceListener<C>> listeners = new ArrayList<>();

public DropwizardTestSupport(Class<? extends Application<C>> applicationClass,
@Nullable String configPath,
ConfigOverride... configOverrides) {
this(applicationClass, configPath, Optional.empty(), configOverrides);
@Nullable String configPath,
ConfigOverride... configOverrides) {
this(applicationClass, configPath, (String) null, configOverrides);
}

public DropwizardTestSupport(Class<? extends Application<C>> applicationClass,
@Nullable String configPath,
ConfigurationSourceProvider configSourceProvider,
ConfigOverride... configOverrides) {
this(applicationClass, configPath, configSourceProvider, null, configOverrides);
}

public DropwizardTestSupport(Class<? extends Application<C>> applicationClass, @Nullable String configPath,
Optional<String> customPropertyPrefix, ConfigOverride... configOverrides) {
/**
* @deprecated Use {@link #DropwizardTestSupport(Class, String, String, ConfigOverride...)} instead.
*/
@Deprecated
public DropwizardTestSupport(Class<? extends Application<C>> applicationClass,
@Nullable String configPath,
Optional<String> customPropertyPrefix,
ConfigOverride... configOverrides) {
this(applicationClass, configPath, customPropertyPrefix.orElse(null), ServerCommand::new, configOverrides);
}

public DropwizardTestSupport(Class<? extends Application<C>> applicationClass,
@Nullable String configPath,
ConfigurationSourceProvider configSourceProvider,
@Nullable String customPropertyPrefix,
ConfigOverride... configOverrides) {
this(applicationClass, configPath, configSourceProvider, customPropertyPrefix, ServerCommand::new, configOverrides);
}

public DropwizardTestSupport(Class<? extends Application<C>> applicationClass,
@Nullable String configPath,
@Nullable String customPropertyPrefix,
ConfigOverride... configOverrides) {
this(applicationClass, configPath, customPropertyPrefix, ServerCommand::new, configOverrides);
}

public DropwizardTestSupport(Class<? extends Application<C>> applicationClass, @Nullable String configPath,
/**
* @deprecated Use {@link #DropwizardTestSupport(Class, String, String, Function, ConfigOverride...)} instead.
*/
@Deprecated
public DropwizardTestSupport(Class<? extends Application<C>> applicationClass,
@Nullable String configPath,
Optional<String> customPropertyPrefix,
Function<Application<C>, Command> commandInstantiator,
ConfigOverride... configOverrides) {
this(applicationClass, configPath, customPropertyPrefix.orElse(null), commandInstantiator, configOverrides);
}

public DropwizardTestSupport(Class<? extends Application<C>> applicationClass,
@Nullable String configPath,
@Nullable String customPropertyPrefix,
Function<Application<C>, Command> commandInstantiator,
ConfigOverride... configOverrides) {
this(applicationClass, configPath, new FileConfigurationSourceProvider(), customPropertyPrefix, commandInstantiator, configOverrides);
}

public DropwizardTestSupport(Class<? extends Application<C>> applicationClass,
@Nullable String configPath,
ConfigurationSourceProvider configSourceProvider,
@Nullable String customPropertyPrefix,
Function<Application<C>, Command> commandInstantiator,
ConfigOverride... configOverrides) {
this.applicationClass = applicationClass;
this.configPath = configPath;
this.configSourceProvider = configSourceProvider;
this.configOverrides = configOverrides == null ? Collections.emptySet() : Sets.of(configOverrides);
this.customPropertyPrefix = customPropertyPrefix;
explicitConfig = false;
this.explicitConfig = false;
this.commandInstantiator = commandInstantiator;
}

Expand Down Expand Up @@ -117,17 +173,18 @@ public DropwizardTestSupport(Class<? extends Application<C>> applicationClass,
* start the Application
*/
public DropwizardTestSupport(Class<? extends Application<C>> applicationClass,
C configuration, Function<Application<C>,
Command> commandInstantiator) {
@Nullable C configuration,
Function<Application<C>, Command> commandInstantiator) {
if (configuration == null) {
throw new IllegalArgumentException("Can not pass null configuration for explicitly configured instance");
}
this.applicationClass = applicationClass;
configPath = "";
configOverrides = Collections.emptySet();
customPropertyPrefix = Optional.empty();
this.configPath = "";
this.configSourceProvider = new FileConfigurationSourceProvider();
this.configOverrides = Collections.emptySet();
this.customPropertyPrefix = null;
this.configuration = configuration;
explicitConfig = true;
this.explicitConfig = true;
this.commandInstantiator = commandInstantiator;
}

Expand All @@ -145,7 +202,7 @@ public void onRun(C configuration, Environment environment, DropwizardTestSuppor
});
}

public void before() {
public void before() throws Exception {
applyConfigOverrides();
try {
startIfRequired();
Expand All @@ -155,6 +212,7 @@ public void before() {
// manually call after as junit does not call the after method if
// the `before` method throws.
after();

throw e;
}
}
Expand Down Expand Up @@ -187,8 +245,10 @@ private void stopIfRequired() {
}
}

// Don't leak appenders into other test cases
requireNonNull(configuration).getLoggingFactory().reset();
// Don't leak logging appenders into other test cases
if (configuration != null) {
configuration.getLoggingFactory().reset();
}
}

private void applyConfigOverrides() {
Expand All @@ -203,58 +263,54 @@ private void resetConfigOverrides() {
}
}

private void startIfRequired() {
private void startIfRequired() throws Exception {
if (jettyServer != null) {
return;
}

try {
application = newApplication();

final Bootstrap<C> bootstrap = new Bootstrap<C>(getApplication()) {
@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<C> listener : listeners) {
try {
listener.onRun(configuration, environment, DropwizardTestSupport.this);
} catch (Exception ex) {
throw new RuntimeException("Error running app rule start listener", ex);
}
application = newApplication();

final Bootstrap<C> bootstrap = new Bootstrap<C>(getApplication()) {
@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<C> listener : listeners) {
try {
listener.onRun(configuration, environment, DropwizardTestSupport.this);
} catch (Exception ex) {
throw new RuntimeException("Error running app rule start listener", ex);
}
}
};

getApplication().initialize(bootstrap);

if (explicitConfig) {
bootstrap.setConfigurationFactoryFactory((klass, validator, objectMapper, propertyPrefix) ->
new POJOConfigurationFactory<>(getConfiguration()));
} else if (customPropertyPrefix.isPresent()) {
bootstrap.setConfigurationFactoryFactory((klass, validator, objectMapper, propertyPrefix) ->
new YamlConfigurationFactory<>(klass, validator, objectMapper, customPropertyPrefix.get()));
}
};

getApplication().initialize(bootstrap);
bootstrap.setConfigurationSourceProvider(configSourceProvider);

if (explicitConfig) {
bootstrap.setConfigurationFactoryFactory((klass, validator, objectMapper, propertyPrefix) ->
new POJOConfigurationFactory<>(getConfiguration()));
} else if (customPropertyPrefix != null) {
@NotNull
final String prefix = customPropertyPrefix;
bootstrap.setConfigurationFactoryFactory((klass, validator, objectMapper, propertyPrefix) ->
new YamlConfigurationFactory<>(klass, validator, objectMapper, prefix));
}

final Command command = commandInstantiator.apply(application);

final Map<String, Object> file;
if (!Strings.isNullOrEmpty(configPath)) {
file = Collections.singletonMap("file", configPath);
} else {
file = Collections.emptyMap();
}
final Namespace namespace = new Namespace(file);

command.run(bootstrap, namespace);
} catch (Exception e) {
if (e instanceof RuntimeException) {
throw (RuntimeException) e;
}
throw new RuntimeException(e);
final Map<String, Object> namespaceAttributes;
if (!Strings.isNullOrEmpty(configPath)) {
namespaceAttributes = Collections.singletonMap("file", configPath);
} else {
namespaceAttributes = Collections.emptyMap();
}

final Namespace namespace = new Namespace(namespaceAttributes);
final Command command = commandInstantiator.apply(application);
command.run(bootstrap, namespace);
}

public C getConfiguration() {
Expand Down
Expand Up @@ -5,6 +5,7 @@
import io.dropwizard.Configuration;
import io.dropwizard.cli.Command;
import io.dropwizard.cli.ServerCommand;
import io.dropwizard.configuration.ConfigurationSourceProvider;
import io.dropwizard.jersey.jackson.JacksonFeature;
import io.dropwizard.lifecycle.Managed;
import io.dropwizard.setup.Environment;
Expand Down Expand Up @@ -90,19 +91,71 @@ public DropwizardAppRule(Class<? extends Application<C>> applicationClass) {
public DropwizardAppRule(Class<? extends Application<C>> applicationClass,
@Nullable String configPath,
ConfigOverride... configOverrides) {
this(applicationClass, configPath, Optional.empty(), configOverrides);
this(applicationClass, configPath, (String) null, configOverrides);
}

public DropwizardAppRule(Class<? extends Application<C>> applicationClass, @Nullable String configPath,
Optional<String> customPropertyPrefix, ConfigOverride... configOverrides) {
public DropwizardAppRule(Class<? extends Application<C>> applicationClass,
@Nullable String configPath,
ConfigurationSourceProvider configSourceProvider,
ConfigOverride... configOverrides) {
this(applicationClass, configPath, configSourceProvider, null, configOverrides);
}

/**
* @deprecated Use {@link #DropwizardAppRule(Class, String, String, ConfigOverride...)} instead.
*/
@Deprecated
public DropwizardAppRule(Class<? extends Application<C>> applicationClass,
@Nullable String configPath,
Optional<String> customPropertyPrefix,
ConfigOverride... configOverrides) {
this(applicationClass, configPath, customPropertyPrefix.orElse(null), configOverrides);
}

public DropwizardAppRule(Class<? extends Application<C>> applicationClass,
@Nullable String configPath,
@Nullable String customPropertyPrefix,
ConfigOverride... configOverrides) {
this(applicationClass, configPath, customPropertyPrefix, ServerCommand::new, configOverrides);
}

public DropwizardAppRule(Class<? extends Application<C>> applicationClass, @Nullable String configPath,
Optional<String> customPropertyPrefix, Function<Application<C>,
Command> commandInstantiator, ConfigOverride... configOverrides) {
this(new DropwizardTestSupport<>(applicationClass, configPath, customPropertyPrefix, commandInstantiator,
configOverrides));
public DropwizardAppRule(Class<? extends Application<C>> applicationClass,
@Nullable String configPath,
ConfigurationSourceProvider configSourceProvider,
@Nullable String customPropertyPrefix,
ConfigOverride... configOverrides) {
this(applicationClass, configPath, configSourceProvider, customPropertyPrefix, ServerCommand::new, configOverrides);
}

/**
* @deprecated Use {@link #DropwizardAppRule(Class, String, String, Function, ConfigOverride...)} instead.
*/
@Deprecated
public DropwizardAppRule(Class<? extends Application<C>> applicationClass,
@Nullable String configPath,
Optional<String> customPropertyPrefix,
Function<Application<C>, Command> commandInstantiator,
ConfigOverride... configOverrides) {
this(applicationClass, configPath, customPropertyPrefix.orElse(null), commandInstantiator, configOverrides);
}

public DropwizardAppRule(Class<? extends Application<C>> applicationClass,
@Nullable String configPath,
@Nullable String customPropertyPrefix,
Function<Application<C>,
Command> commandInstantiator,
ConfigOverride... configOverrides) {
this(new DropwizardTestSupport<>(applicationClass, configPath, customPropertyPrefix, commandInstantiator, configOverrides));
}

public DropwizardAppRule(Class<? extends Application<C>> applicationClass,
@Nullable String configPath,
ConfigurationSourceProvider configSourceProvider,
@Nullable String customPropertyPrefix,
Function<Application<C>,
Command> commandInstantiator,
ConfigOverride... configOverrides) {
this(new DropwizardTestSupport<>(applicationClass, configPath, configSourceProvider, customPropertyPrefix, commandInstantiator, configOverrides));
}

/**
Expand Down Expand Up @@ -155,7 +208,7 @@ public void onRun(C configuration, Environment environment, DropwizardAppRule<C>
}

@Override
protected void before() {
protected void before() throws Exception {
if (recursiveCallCount.getAndIncrement() == 0) {
testSupport.before();
}
Expand Down

0 comments on commit 86abeb0

Please sign in to comment.