Support for random ports in @IntegrationTest
User can now set up default properties in the Environment using
@IntegrationTest("foo:bar", "x:y") etc. Using "server.port:0" you
can get Tomcat or Jetty to spin up on a random port. We also add
a test listener that populates "local.server.port" with the actual
port the server started on so you can @Value("${local.server.port}")
inject it into the test case.
See gh-607 (this should make the extension of that PR to samples
much easier)
This commit is contained in:
parent
2d16c59147
commit
559009b8cf
|
|
@ -16,11 +16,15 @@
|
|||
|
||||
package sample.ui;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.test.IntegrationTest;
|
||||
import org.springframework.boot.test.TestRestTemplate;
|
||||
import org.springframework.boot.test.SpringApplicationConfiguration;
|
||||
import org.springframework.boot.test.TestRestTemplate;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
|
|
@ -28,9 +32,6 @@ import org.springframework.test.annotation.DirtiesContext;
|
|||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.test.context.web.WebAppConfiguration;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
* Basic integration tests for demo application.
|
||||
*
|
||||
|
|
@ -39,14 +40,17 @@ import static org.junit.Assert.assertTrue;
|
|||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@SpringApplicationConfiguration(classes = SampleWebStaticApplication.class)
|
||||
@WebAppConfiguration
|
||||
@IntegrationTest
|
||||
@IntegrationTest("server.port=0")
|
||||
@DirtiesContext
|
||||
public class SampleWebStaticApplicationTests {
|
||||
|
||||
@Value("${local.server.port}")
|
||||
private int port = 0;
|
||||
|
||||
@Test
|
||||
public void testHome() throws Exception {
|
||||
ResponseEntity<String> entity = new TestRestTemplate().getForEntity(
|
||||
"http://localhost:8080", String.class);
|
||||
"http://localhost:" + port, String.class);
|
||||
assertEquals(HttpStatus.OK, entity.getStatusCode());
|
||||
assertTrue("Wrong body (title doesn't match):\n" + entity.getBody(), entity
|
||||
.getBody().contains("<title>Static"));
|
||||
|
|
@ -55,7 +59,7 @@ public class SampleWebStaticApplicationTests {
|
|||
@Test
|
||||
public void testCss() throws Exception {
|
||||
ResponseEntity<String> entity = new TestRestTemplate().getForEntity(
|
||||
"http://localhost:8080/webjars/bootstrap/3.0.3/css/bootstrap.min.css",
|
||||
"http://localhost:" + port + "/webjars/bootstrap/3.0.3/css/bootstrap.min.css",
|
||||
String.class);
|
||||
assertEquals(HttpStatus.OK, entity.getStatusCode());
|
||||
assertTrue("Wrong body:\n" + entity.getBody(), entity.getBody().contains("body"));
|
||||
|
|
|
|||
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* Copyright 2012-2013 the original author or authors.
|
||||
*
|
||||
* 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 org.springframework.boot.test;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.context.embedded.EmbeddedWebApplicationContext;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.test.context.TestContext;
|
||||
import org.springframework.test.context.support.AbstractTestExecutionListener;
|
||||
|
||||
/**
|
||||
* Listener that injects the server port (if one is discoverable from the application
|
||||
* context)into a field annotated with {@link Value @Value("dynamic.port")}.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*/
|
||||
public class EmbeddedServletContainerListener extends AbstractTestExecutionListener {
|
||||
|
||||
@Override
|
||||
public void prepareTestInstance(TestContext testContext) throws Exception {
|
||||
ApplicationContext context = testContext.getApplicationContext();
|
||||
if (!(context instanceof EmbeddedWebApplicationContext)) {
|
||||
return;
|
||||
}
|
||||
EmbeddedWebApplicationContext embedded = (EmbeddedWebApplicationContext) context;
|
||||
final int port = embedded.getEmbeddedServletContainer().getPort();
|
||||
EnvironmentTestUtils.addEnvironment(embedded, "local.server.port:" + port);
|
||||
}
|
||||
}
|
||||
|
|
@ -55,16 +55,29 @@ public abstract class EnvironmentTestUtils {
|
|||
*/
|
||||
public static void addEnvironment(ConfigurableEnvironment environment,
|
||||
String... pairs) {
|
||||
addEnvironment("test", environment, pairs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add additional (high priority) values to an {@link Environment}. Name-value pairs
|
||||
* can be specified with colon (":") or equals ("=") separators.
|
||||
*
|
||||
* @param environment the environment to modify
|
||||
* @param name the property source name
|
||||
* @param pairs the name:value pairs
|
||||
*/
|
||||
public static void addEnvironment(String name, ConfigurableEnvironment environment,
|
||||
String... pairs) {
|
||||
MutablePropertySources sources = environment.getPropertySources();
|
||||
Map<String, Object> map;
|
||||
if (!sources.contains("test")) {
|
||||
if (!sources.contains(name)) {
|
||||
map = new HashMap<String, Object>();
|
||||
MapPropertySource source = new MapPropertySource("test", map);
|
||||
MapPropertySource source = new MapPropertySource(name, map);
|
||||
sources.addFirst(source);
|
||||
}
|
||||
else {
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, Object> value = (Map<String, Object>) sources.get("test")
|
||||
Map<String, Object> value = (Map<String, Object>) sources.get(name)
|
||||
.getSource();
|
||||
map = value;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -40,9 +40,11 @@ import org.springframework.test.context.transaction.TransactionalTestExecutionLi
|
|||
@Target(ElementType.TYPE)
|
||||
// Leave out the ServletTestExecutionListener because it only deals with Mock* servlet
|
||||
// stuff. A real embedded application will not need the mocks.
|
||||
@TestExecutionListeners(listeners = { DependencyInjectionTestExecutionListener.class,
|
||||
@TestExecutionListeners(listeners = { EmbeddedServletContainerListener.class,
|
||||
DependencyInjectionTestExecutionListener.class,
|
||||
DirtiesContextTestExecutionListener.class,
|
||||
TransactionalTestExecutionListener.class })
|
||||
public @interface IntegrationTest {
|
||||
|
||||
String[] value() default "";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ package org.springframework.boot.test;
|
|||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
|
|
@ -135,16 +136,32 @@ public class SpringApplicationContextLoader extends AbstractContextLoader {
|
|||
|
||||
private Map<String, Object> getArgs(MergedContextConfiguration mergedConfig) {
|
||||
Map<String, Object> args = new LinkedHashMap<String, Object>();
|
||||
if (AnnotationUtils.findAnnotation(mergedConfig.getTestClass(),
|
||||
IntegrationTest.class) == null) {
|
||||
// JMX bean names will clash if the same bean is used in multiple contexts
|
||||
args.put("spring.jmx.enabled", "false");
|
||||
IntegrationTest annotation = AnnotationUtils.findAnnotation(
|
||||
mergedConfig.getTestClass(), IntegrationTest.class);
|
||||
if (annotation == null) {
|
||||
// Not running an embedded server, just setting up web context
|
||||
args.put("server.port", "-1");
|
||||
}
|
||||
// JMX bean names will clash if the same bean is used in multiple contexts
|
||||
args.put("spring.jmx.enabled", "false");
|
||||
else {
|
||||
args.putAll(extractProperties(annotation.value()));
|
||||
}
|
||||
return args;
|
||||
}
|
||||
|
||||
private Map<String, String> extractProperties(String[] values) {
|
||||
Map<String, String> map = new HashMap<String, String>();
|
||||
for (String pair : values) {
|
||||
int index = pair.indexOf(":");
|
||||
index = index < 0 ? index = pair.indexOf("=") : index;
|
||||
String key = pair.substring(0, index > 0 ? index : pair.length());
|
||||
String value = index > 0 ? pair.substring(index + 1) : "";
|
||||
map.put(key.trim(), value.trim());
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
private List<ApplicationContextInitializer<?>> getInitializers(
|
||||
MergedContextConfiguration mergedConfig, SpringApplication application) {
|
||||
List<ApplicationContextInitializer<?>> initializers = new ArrayList<ApplicationContextInitializer<?>>();
|
||||
|
|
|
|||
|
|
@ -18,11 +18,13 @@ package org.springframework.boot.test;
|
|||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.context.embedded.EmbeddedServletContainerFactory;
|
||||
import org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory;
|
||||
import org.springframework.boot.test.SpringApplicationIntegrationTestTests.Config;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.test.context.web.WebAppConfiguration;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
|
|
@ -32,6 +34,7 @@ import org.springframework.web.servlet.DispatcherServlet;
|
|||
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotEquals;
|
||||
|
||||
/**
|
||||
* Tests for {@link IntegrationTest}
|
||||
|
|
@ -41,13 +44,18 @@ import static org.junit.Assert.assertEquals;
|
|||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@SpringApplicationConfiguration(classes = Config.class)
|
||||
@WebAppConfiguration
|
||||
@IntegrationTest
|
||||
@IntegrationTest("server.port=0")
|
||||
public class SpringApplicationIntegrationTestTests {
|
||||
|
||||
@Value("${local.server.port}")
|
||||
private int port = 0;
|
||||
|
||||
@Test
|
||||
public void runAndTestHttpEndpoint() {
|
||||
String body = new RestTemplate().getForObject("http://localhost:8080/",
|
||||
String.class);
|
||||
assertNotEquals(8080, this.port);
|
||||
assertNotEquals(0, this.port);
|
||||
String body = new RestTemplate().getForObject("http://localhost:" + this.port
|
||||
+ "/", String.class);
|
||||
assertEquals("Hello World", body);
|
||||
}
|
||||
|
||||
|
|
@ -56,6 +64,9 @@ public class SpringApplicationIntegrationTestTests {
|
|||
@RestController
|
||||
protected static class Config {
|
||||
|
||||
@Value("${server.port:8080}")
|
||||
private int port = 8080;
|
||||
|
||||
@Bean
|
||||
public DispatcherServlet dispatcherServlet() {
|
||||
return new DispatcherServlet();
|
||||
|
|
@ -63,7 +74,14 @@ public class SpringApplicationIntegrationTestTests {
|
|||
|
||||
@Bean
|
||||
public EmbeddedServletContainerFactory embeddedServletContainer() {
|
||||
return new TomcatEmbeddedServletContainerFactory();
|
||||
TomcatEmbeddedServletContainerFactory factory = new TomcatEmbeddedServletContainerFactory();
|
||||
factory.setPort(this.port);
|
||||
return factory;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public static PropertySourcesPlaceholderConfigurer propertyPlaceholder() {
|
||||
return new PropertySourcesPlaceholderConfigurer();
|
||||
}
|
||||
|
||||
@RequestMapping("/")
|
||||
|
|
|
|||
Loading…
Reference in New Issue