From 5fe9ef69c7ace858ab3ea1495f93f16502966d85 Mon Sep 17 00:00:00 2001 From: Dave Syer Date: Wed, 2 Oct 2013 15:07:04 -0400 Subject: [PATCH] Add SpringApplicationContextLoader --- .../jpa/HibernateJpaAutoConfiguration.java | 2 - .../spring-boot-sample-data-jpa/pom.xml | 6 + .../data/jpa/AbstractIntegrationTests.java | 39 ------- .../jpa/SampleDataJpaApplicationTests.java | 26 +++-- .../CityRepositoryIntegrationTests.java | 19 ++- .../HotelRepositoryIntegrationTests.java | 21 ++-- .../resources/application-scratch.properties | 1 + .../test/SpringApplicationContextLoader.java | 109 ++++++++++++++++++ 8 files changed, 161 insertions(+), 62 deletions(-) delete mode 100644 spring-boot-samples/spring-boot-sample-data-jpa/src/test/java/org/springframework/boot/sample/data/jpa/AbstractIntegrationTests.java create mode 100644 spring-boot-samples/spring-boot-sample-data-jpa/src/test/resources/application-scratch.properties create mode 100644 spring-boot/src/test/java/org/springframework/boot/test/SpringApplicationContextLoader.java diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaAutoConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaAutoConfiguration.java index 38c1bc96e6..e87d5fdc65 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaAutoConfiguration.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaAutoConfiguration.java @@ -65,8 +65,6 @@ public class HibernateJpaAutoConfiguration extends JpaBaseConfiguration { protected void configure( LocalContainerEntityManagerFactoryBean entityManagerFactoryBean) { Map properties = entityManagerFactoryBean.getJpaPropertyMap(); - properties.put("hibernate.cache.provider_class", this.environment.getProperty( - "cache-provider", "org.hibernate.cache.HashtableCacheProvider")); properties.put("hibernate.ejb.naming_strategy", this.environment.getProperty( "naming-strategy", ImprovedNamingStrategy.class.getName())); String ddlAuto = this.environment.getProperty("ddl-auto", "none"); diff --git a/spring-boot-samples/spring-boot-sample-data-jpa/pom.xml b/spring-boot-samples/spring-boot-sample-data-jpa/pom.xml index 8a710058a5..d2bea34994 100644 --- a/spring-boot-samples/spring-boot-sample-data-jpa/pom.xml +++ b/spring-boot-samples/spring-boot-sample-data-jpa/pom.xml @@ -22,6 +22,12 @@ ${project.groupId} spring-boot-starter-web + + ${project.groupId} + spring-boot + tests + ${spring-boot.version} + org.hsqldb hsqldb diff --git a/spring-boot-samples/spring-boot-sample-data-jpa/src/test/java/org/springframework/boot/sample/data/jpa/AbstractIntegrationTests.java b/spring-boot-samples/spring-boot-sample-data-jpa/src/test/java/org/springframework/boot/sample/data/jpa/AbstractIntegrationTests.java deleted file mode 100644 index 01d5fb3734..0000000000 --- a/spring-boot-samples/spring-boot-sample-data-jpa/src/test/java/org/springframework/boot/sample/data/jpa/AbstractIntegrationTests.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 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.sample.data.jpa; - -import org.junit.runner.RunWith; -import org.springframework.boot.SpringApplication; -import org.springframework.boot.context.initializer.ConfigFileApplicationContextInitializer; -import org.springframework.boot.context.initializer.LoggingApplicationContextInitializer; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; -import org.springframework.test.context.web.WebAppConfiguration; - -/** - * Base class for integration tests. Mimics the behaviour of - * {@link SpringApplication#run(String...)}. - * - * @author Oliver Gierke - */ -@RunWith(SpringJUnit4ClassRunner.class) -@WebAppConfiguration -@ContextConfiguration(classes = SampleDataJpaApplication.class, initializers = { - ConfigFileApplicationContextInitializer.class, - LoggingApplicationContextInitializer.class }) -public abstract class AbstractIntegrationTests { - -} diff --git a/spring-boot-samples/spring-boot-sample-data-jpa/src/test/java/org/springframework/boot/sample/data/jpa/SampleDataJpaApplicationTests.java b/spring-boot-samples/spring-boot-sample-data-jpa/src/test/java/org/springframework/boot/sample/data/jpa/SampleDataJpaApplicationTests.java index fbc1a23e80..1bef1cd21c 100644 --- a/spring-boot-samples/spring-boot-sample-data-jpa/src/test/java/org/springframework/boot/sample/data/jpa/SampleDataJpaApplicationTests.java +++ b/spring-boot-samples/spring-boot-sample-data-jpa/src/test/java/org/springframework/boot/sample/data/jpa/SampleDataJpaApplicationTests.java @@ -1,22 +1,32 @@ package org.springframework.boot.sample.data.jpa; -import org.junit.Before; -import org.junit.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.test.web.servlet.MockMvc; -import org.springframework.test.web.servlet.setup.MockMvcBuilders; -import org.springframework.web.context.WebApplicationContext; - import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.SpringApplicationContextLoader; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.web.WebAppConfiguration; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import org.springframework.web.context.WebApplicationContext; + /** * Integration test to run the application. * * @author Oliver Gierke */ -public class SampleDataJpaApplicationTests extends AbstractIntegrationTests { +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(classes = SampleDataJpaApplication.class, loader=SpringApplicationContextLoader.class) +@WebAppConfiguration +@ActiveProfiles("scratch") // Separate profile for web tests to avoid clashing databases +public class SampleDataJpaApplicationTests { @Autowired private WebApplicationContext context; diff --git a/spring-boot-samples/spring-boot-sample-data-jpa/src/test/java/org/springframework/boot/sample/data/jpa/service/CityRepositoryIntegrationTests.java b/spring-boot-samples/spring-boot-sample-data-jpa/src/test/java/org/springframework/boot/sample/data/jpa/service/CityRepositoryIntegrationTests.java index f40848f955..981bf09010 100644 --- a/spring-boot-samples/spring-boot-sample-data-jpa/src/test/java/org/springframework/boot/sample/data/jpa/service/CityRepositoryIntegrationTests.java +++ b/spring-boot-samples/spring-boot-sample-data-jpa/src/test/java/org/springframework/boot/sample/data/jpa/service/CityRepositoryIntegrationTests.java @@ -15,22 +15,29 @@ */ package org.springframework.boot.sample.data.jpa.service; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.greaterThan; +import static org.junit.Assert.assertThat; + import org.junit.Test; +import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.sample.data.jpa.AbstractIntegrationTests; +import org.springframework.boot.sample.data.jpa.SampleDataJpaApplication; import org.springframework.boot.sample.data.jpa.domain.City; +import org.springframework.boot.test.SpringApplicationContextLoader; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; - -import static org.hamcrest.CoreMatchers.is; -import static org.junit.Assert.assertThat; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; /** * Integration tests for {@link CityRepository}. * * @author Oliver Gierke */ -public class CityRepositoryIntegrationTests extends AbstractIntegrationTests { +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(classes = SampleDataJpaApplication.class, loader=SpringApplicationContextLoader.class) +public class CityRepositoryIntegrationTests { @Autowired CityRepository repository; @@ -39,6 +46,6 @@ public class CityRepositoryIntegrationTests extends AbstractIntegrationTests { public void findsFirstPageOfCities() { Page cities = this.repository.findAll(new PageRequest(0, 10)); - assertThat(cities.getTotalElements(), is(21L)); + assertThat(cities.getTotalElements(), is(greaterThan(20L))); } } diff --git a/spring-boot-samples/spring-boot-sample-data-jpa/src/test/java/org/springframework/boot/sample/data/jpa/service/HotelRepositoryIntegrationTests.java b/spring-boot-samples/spring-boot-sample-data-jpa/src/test/java/org/springframework/boot/sample/data/jpa/service/HotelRepositoryIntegrationTests.java index 295670c172..37bf385ed7 100644 --- a/spring-boot-samples/spring-boot-sample-data-jpa/src/test/java/org/springframework/boot/sample/data/jpa/service/HotelRepositoryIntegrationTests.java +++ b/spring-boot-samples/spring-boot-sample-data-jpa/src/test/java/org/springframework/boot/sample/data/jpa/service/HotelRepositoryIntegrationTests.java @@ -15,30 +15,37 @@ */ package org.springframework.boot.sample.data.jpa.service; +import static org.hamcrest.Matchers.hasSize; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.greaterThan; +import static org.junit.Assert.assertThat; + import java.util.List; import org.junit.Test; +import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.sample.data.jpa.AbstractIntegrationTests; +import org.springframework.boot.sample.data.jpa.SampleDataJpaApplication; import org.springframework.boot.sample.data.jpa.domain.City; import org.springframework.boot.sample.data.jpa.domain.Hotel; import org.springframework.boot.sample.data.jpa.domain.HotelSummary; import org.springframework.boot.sample.data.jpa.domain.Rating; import org.springframework.boot.sample.data.jpa.domain.RatingCount; +import org.springframework.boot.test.SpringApplicationContextLoader; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Sort.Direction; - -import static org.hamcrest.Matchers.hasSize; -import static org.hamcrest.Matchers.is; -import static org.junit.Assert.assertThat; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; /** * Integration tests for {@link HotelRepository}. * * @author Oliver Gierke */ -public class HotelRepositoryIntegrationTests extends AbstractIntegrationTests { +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(classes = SampleDataJpaApplication.class, loader=SpringApplicationContextLoader.class) +public class HotelRepositoryIntegrationTests { @Autowired CityRepository cityRepository; @@ -61,6 +68,6 @@ public class HotelRepositoryIntegrationTests extends AbstractIntegrationTests { List counts = this.repository.findRatingCounts(hotel); assertThat(counts, hasSize(1)); assertThat(counts.get(0).getRating(), is(Rating.AVERAGE)); - assertThat(counts.get(0).getCount(), is(2L)); + assertThat(counts.get(0).getCount(), is(greaterThan(1L))); } } diff --git a/spring-boot-samples/spring-boot-sample-data-jpa/src/test/resources/application-scratch.properties b/spring-boot-samples/spring-boot-sample-data-jpa/src/test/resources/application-scratch.properties new file mode 100644 index 0000000000..78831d76bd --- /dev/null +++ b/spring-boot-samples/spring-boot-sample-data-jpa/src/test/resources/application-scratch.properties @@ -0,0 +1 @@ +spring.datasource.url: jdbc:hsqldb:mem:scratchdb diff --git a/spring-boot/src/test/java/org/springframework/boot/test/SpringApplicationContextLoader.java b/spring-boot/src/test/java/org/springframework/boot/test/SpringApplicationContextLoader.java new file mode 100644 index 0000000000..78e6a14205 --- /dev/null +++ b/spring-boot/src/test/java/org/springframework/boot/test/SpringApplicationContextLoader.java @@ -0,0 +1,109 @@ +/* + * 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 java.util.ArrayList; +import java.util.Arrays; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; + +import org.springframework.beans.BeanUtils; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.context.initializer.ServletContextApplicationContextInitializer; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextInitializer; +import org.springframework.mock.web.MockServletContext; +import org.springframework.test.context.ContextLoader; +import org.springframework.test.context.MergedContextConfiguration; +import org.springframework.test.context.support.AbstractContextLoader; +import org.springframework.test.context.web.WebAppConfiguration; +import org.springframework.test.context.web.WebMergedContextConfiguration; +import org.springframework.util.ObjectUtils; +import org.springframework.util.StringUtils; +import org.springframework.web.context.support.GenericWebApplicationContext; + +/** + * A {@link ContextLoader} that can be used to test Spring Boot applications (those that + * normally startup using {@link SpringApplication}). Never starts an embedded web server, + * but detects the {@link WebAppConfiguration @WebAppConfiguration} annotation on the test + * class and only creates a web application context if it is present. Non-web features, + * like a repository layer, can be tested cleanly by simply not marking the test + * class @WebAppConfiguration. + * + *

+ * If @ActiveProfiles are provided in the test class they will be used to + * create the application context. + * + * @author Dave Syer + * + */ +public class SpringApplicationContextLoader extends AbstractContextLoader { + + @Override + public ApplicationContext loadContext(MergedContextConfiguration mergedConfig) + throws Exception { + + Set sources = new LinkedHashSet(); + sources.addAll(Arrays.asList(mergedConfig.getClasses())); + sources.addAll(Arrays.asList(mergedConfig.getLocations())); + SpringApplication application = new SpringApplication(); + application.setSources(sources); + + Set args = new LinkedHashSet(); + if (!ObjectUtils.isEmpty(mergedConfig.getActiveProfiles())) { + args.add("--spring.profiles.active=" + + StringUtils.arrayToCommaDelimitedString(mergedConfig + .getActiveProfiles())); + } + // Not running an embedded server, just setting up web context + args.add("--server.port=0"); + args.add("--management.port=0"); + application.setDefaultArgs(args.toArray(new String[args.size()])); + List> initializers = new ArrayList>( + application.getInitializers()); + for (Class> type : mergedConfig + .getContextInitializerClasses()) { + initializers.add(BeanUtils.instantiate(type)); + } + if (mergedConfig instanceof WebMergedContextConfiguration) { + WebMergedContextConfiguration webConfig = (WebMergedContextConfiguration) mergedConfig; + MockServletContext servletContext = new MockServletContext( + webConfig.getResourceBasePath()); + initializers.add(0, new ServletContextApplicationContextInitializer( + servletContext)); + application.setApplicationContextClass(GenericWebApplicationContext.class); + } + else { + application.setWebEnvironment(false); + } + application.setInitializers(initializers); + return application.run(); + } + + @Override + public ApplicationContext loadContext(String... locations) throws Exception { + throw new UnsupportedOperationException( + "SpringApplicationContextLoader does not support the loadContext(String...) method"); + } + + @Override + protected String getResourceSuffix() { + return "-context.xml"; + } + +}