Add "testing your auto-configuration" doc section

Closes gh-10011
This commit is contained in:
Stephane Nicoll 2017-12-12 17:01:40 +01:00
parent 175f451b0f
commit e274f29018
5 changed files with 241 additions and 0 deletions

View File

@ -53,6 +53,7 @@ Phillip Webb; Dave Syer; Josh Long; Stéphane Nicoll; Rob Winch; Andy Wilkinson;
:propdeps-plugin: https://github.com/spring-projects/gradle-plugins/tree/master/propdeps-plugin
:ant-manual: http://ant.apache.org/manual
:code-examples: ../java/org/springframework/boot
:test-examples: ../../test/java/org/springframework/boot
:gradle-user-guide: https://docs.gradle.org/4.2.1/userguide
:hibernate-documentation: http://docs.jboss.org/hibernate/orm/5.2/userguide/html_single/Hibernate_User_Guide.html
:jetty-documentation: https://www.eclipse.org/jetty/documentation/9.4.x

View File

@ -7101,6 +7101,66 @@ result of a {spring-reference}core.html#expressions[SpEL expression].
[[boot-features-test-autoconfig]]
=== Testing your auto-configuration
An auto-configuration can be affected by many factors: user configuration (`@Bean`
definition and `Environment` customization), condition evaluation (presence of a
particular library), etc. Concretely, each test should create a well-defined
`ApplicationContext` that represents a combination of those customizations and
`ApplicationContextRunner` provides a great way to achieve that.
`ApplicationContextRunner` is usually defined as a field of the test class to gather the
base, common configuration. The following makes sure that `UserServiceAutoConfiguration`
is always invoked:
[source,java,indent=0]
----
include::{test-examples}/autoconfigure/UserServiceAutoConfigurationTests.java[tag=runner]
----
TIP: If multiple auto-configurations have to be defined, there is no need to order their
declarations as they will be invoked in the exact same order as when running the
application.
Each test can use the runner to represents a particular use case. For instance, the sample
below invokes a user configuration (`UserConfiguration`) and checks that the
auto-configuration backs off properly. Invoking `run` provides a callback context that can
be used with `Assert4J`:
[source,java,indent=0]
----
include::{test-examples}/autoconfigure/UserServiceAutoConfigurationTests.java[tag=test-user-config]
----
It is also possible to easily customize the `Environment`:
[source,java,indent=0]
----
include::{test-examples}/autoconfigure/UserServiceAutoConfigurationTests.java[tag=test-env]
----
==== Simulating a web context
If you need to test an auto-configuration that only operates in a Servlet or Reactive web
application context, use the `WebApplicationContextRunner` or
`ReactiveWebApplicationContextRunner` respectively.
==== Overriding the classpath
It is also possible to test what happens when a particular class and/or package is not
present at runtime. Spring Boot ships with a `FilteredClassLoader` that can easily be used
by the runner. In the example below, we assert that if `UserService` is not present, the
auto-configuration will be properly disabled:
[source,java,indent=0]
----
include::{test-examples}/autoconfigure/UserServiceAutoConfigurationTests.java[tag=test-classloader]
----
[[boot-features-custom-starter]]
=== Creating Your Own Starter
A full Spring Boot starter for a library may contain the following components:

View File

@ -0,0 +1,36 @@
/*
* Copyright 2012-2017 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.autoconfigure;
/**
* Simple service.
*
* @author Stephane Nicoll
*/
class UserService {
private final String name;
UserService(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
}

View File

@ -0,0 +1,59 @@
/*
* Copyright 2012-2017 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.autoconfigure;
import org.springframework.boot.autoconfigure.UserServiceAutoConfiguration.UserProperties;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* Sample auto-configuration.
*
* @author Stephane Nicoll
*/
@Configuration
@ConditionalOnClass(UserService.class)
@EnableConfigurationProperties(UserProperties.class)
public class UserServiceAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public UserService userService(UserProperties properties) {
return new UserService(properties.getName());
}
@ConfigurationProperties("user")
static class UserProperties {
private String name = "test";
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
}
}

View File

@ -0,0 +1,85 @@
/*
* Copyright 2012-2017 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.autoconfigure;
import org.junit.Test;
import org.springframework.boot.test.context.FilteredClassLoader;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link UserServiceAutoConfiguration}.
*
* @author Stephane Nicoll
*/
public class UserServiceAutoConfigurationTests {
// tag::runner[]
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(UserServiceAutoConfiguration.class));
// end::runner[]
// tag::test-env[]
@Test
public void serviceNameCanBeConfigured() {
this.contextRunner.withPropertyValues("user.name=test123")
.run((context) -> {
assertThat(context).hasSingleBean(UserService.class);
assertThat(context.getBean(UserService.class).getName())
.isEqualTo("test123");
});
}
// end::test-env[]
// tag::test-classloader[]
@Test
public void serviceIsIgnoredIfLibraryIsNotPresent() {
this.contextRunner.withClassLoader(new FilteredClassLoader(UserService.class))
.run((context) -> {
assertThat(context).doesNotHaveBean("userService");
});
}
// end::test-classloader[]
// tag::test-user-config[]
@Test
public void defaultServiceBacksOff() {
this.contextRunner.withUserConfiguration(UserConfiguration.class)
.run((context) -> {
assertThat(context).hasSingleBean(UserService.class);
assertThat(context.getBean(UserService.class)).isSameAs(
context.getBean(UserConfiguration.class).myUserService());
});
}
@Configuration
static class UserConfiguration {
@Bean
public UserService myUserService() {
return new UserService("mine");
}
}
// end::test-user-config[]
}