package io.dropwizard.testing.junit;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider;
import io.dropwizard.jackson.Jackson;
import io.dropwizard.jersey.validation.Validators;
import io.dropwizard.logging.BootstrapLogging;
import org.glassfish.jersey.client.ClientConfig;
import org.glassfish.jersey.servlet.ServletProperties;
import org.glassfish.jersey.test.DeploymentContext;
import org.glassfish.jersey.test.JerseyTest;
import org.glassfish.jersey.test.ServletDeploymentContext;
import org.glassfish.jersey.test.inmemory.InMemoryTestContainerFactory;
import org.glassfish.jersey.test.spi.TestContainerFactory;
import org.junit.rules.TestRule;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;
import javax.validation.Validator;
import javax.ws.rs.client.Client;
import javax.ws.rs.client.WebTarget;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.function.Consumer;
/**
* A JUnit {@link TestRule} for testing Jersey resources.
*/
public class ResourceTestRule implements TestRule {
static {
BootstrapLogging.bootstrap();
}
/**
* A {@link ResourceTestRule} builder which enables configuration of a Jersey testing environment.
*/
public static class Builder {
private final Set<Object> singletons = new HashSet<>();
private final Set<Class<?>> providers = new HashSet<>();
private final Map<String, Object> properties = new HashMap<>();
private ObjectMapper mapper = Jackson.newObjectMapper();
private Validator validator = Validators.newValidator();
private Consumer<ClientConfig> clientConfigurator = c -> {
};
private TestContainerFactory testContainerFactory = new InMemoryTestContainerFactory();
private boolean registerDefaultExceptionMappers = true;
public Builder setMapper(ObjectMapper mapper) {
this.mapper = mapper;
return this;
}
public Builder setValidator(Validator validator) {
this.validator = validator;
return this;
}
public Builder setClientConfigurator(Consumer<ClientConfig> clientConfigurator) {
this.clientConfigurator = clientConfigurator;
return this;
}
public Builder addResource(Object resource) {
singletons.add(resource);
return this;
}
public Builder addProvider(Class<?> klass) {
providers.add(klass);
return this;
}
public Builder addProvider(Object provider) {
singletons.add(provider);
return this;
}
public Builder addProperty(String property, Object value) {
properties.put(property, value);
return this;
}
public Builder setTestContainerFactory(TestContainerFactory factory) {
this.testContainerFactory = factory;
return this;
}
public Builder setRegisterDefaultExceptionMappers(boolean value) {
registerDefaultExceptionMappers = value;
return this;
}
/**
* Builds a {@link ResourceTestRule} with a configured Jersey testing environment.
*
* @return a new {@link ResourceTestRule}
*/
public ResourceTestRule build() {
return new ResourceTestRule(new ResourceTestJerseyConfiguration(
singletons, providers, properties, mapper, validator,
clientConfigurator, testContainerFactory, registerDefaultExceptionMappers));
}
}
/**
* Creates a new Jersey testing environment builder for {@link ResourceTestRule}
*
* @return a new {@link Builder}
*/
public static Builder builder() {
return new Builder();
}
private ResourceTestJerseyConfiguration configuration;
private JerseyTest test;
private ResourceTestRule(ResourceTestJerseyConfiguration configuration) {
this.configuration = configuration;
}
public Validator getValidator() {
return configuration.validator;
}
public ObjectMapper getObjectMapper() {
return configuration.mapper;
}
public Consumer<ClientConfig> getClientConfigurator() {
return configuration.clientConfigurator;
}
/**
* Creates a web target to be sent to the resource under testing.
*
* @param path relative path (from tested application base URI) this web target should point to.
* @return the created JAX-RS web target.
*/
public WebTarget target(String path) {
return getJerseyTest().target(path);
}
/**
* Returns the pre-configured {@link Client} for this test. For sending
* requests prefer {@link #target(String)}
*
* @return the {@link JerseyTest} configured {@link Client}
*/
public Client client() {
return test.client();
}
/**
* Returns the underlying {@link JerseyTest}. For sending requests prefer
* {@link #target(String)}.
*
* @return the underlying {@link JerseyTest}
*/
public JerseyTest getJerseyTest() {
return test;
}
@Override
public Statement apply(Statement base, Description description) {
return new Statement() {
@Override
public void evaluate() throws Throwable {
DropwizardTestResourceConfig.CONFIGURATION_REGISTRY.put(configuration.getId(), configuration);
try {
test = new JerseyTest() {
@Override
protected TestContainerFactory getTestContainerFactory() {
return configuration.testContainerFactory;
}
@Override
protected DeploymentContext configureDeployment() {
return ServletDeploymentContext.builder(new DropwizardTestResourceConfig(configuration))
.initParam(ServletProperties.JAXRS_APPLICATION_CLASS,
DropwizardTestResourceConfig.class.getName())
.initParam(DropwizardTestResourceConfig.CONFIGURATION_ID, configuration.getId())
.build();
}
@Override
protected void configureClient(ClientConfig clientConfig) {
final JacksonJsonProvider jsonProvider = new JacksonJsonProvider();
jsonProvider.setMapper(configuration.mapper);
configuration.clientConfigurator.accept(clientConfig);
clientConfig.register(jsonProvider);
}
};
test.setUp();
base.evaluate();
} finally {
DropwizardTestResourceConfig.CONFIGURATION_REGISTRY.remove(configuration.getId());
test.tearDown();
}
}
};
}
}