From ed8f8d59ef9ef73553b9b3acd60b5e8ba618b6d5 Mon Sep 17 00:00:00 2001 From: Madhura Bhave Date: Mon, 7 Mar 2022 18:11:17 -0800 Subject: [PATCH] Document how to structure configurations for efficient slice tests Closes gh-16088 --- .../src/docs/asciidoc/features/testing.adoc | 1 + .../src/docs/asciidoc/howto/testing.adoc | 33 ++++++++++++++ .../testing/slicetests/MyConfiguration.java | 43 +++++++++++++++++++ .../slicetests/MyDatasourceConfiguration.java | 35 +++++++++++++++ .../slicetests/MySecurityConfiguration.java | 33 ++++++++++++++ 5 files changed, 145 insertions(+) create mode 100644 spring-boot-project/spring-boot-docs/src/main/java/org/springframework/boot/docs/howto/testing/slicetests/MyConfiguration.java create mode 100644 spring-boot-project/spring-boot-docs/src/main/java/org/springframework/boot/docs/howto/testing/slicetests/MyDatasourceConfiguration.java create mode 100644 spring-boot-project/spring-boot-docs/src/main/java/org/springframework/boot/docs/howto/testing/slicetests/MySecurityConfiguration.java diff --git a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/features/testing.adoc b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/features/testing.adoc index bdd59886a3..dfb80686f0 100644 --- a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/features/testing.adoc +++ b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/features/testing.adoc @@ -845,6 +845,7 @@ include::{docs-java}/features/testing/springbootapplications/userconfigurationan NOTE: Depending on the complexity of your application, you may either have a single `@Configuration` class for your customizations or one class per domain area. The latter approach lets you enable it in one of your tests, if necessary, with the `@Import` annotation. +See <> for more details on when you might want to enable specific `@Configuration` classes for slice tests. Test slices exclude `@Configuration` classes from scanning. For example, for a `@WebMvcTest`, the following configuration will not include the given `WebMvcConfigurer` bean in the application context loaded by the test slice: diff --git a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/howto/testing.adoc b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/howto/testing.adoc index ab606af1fc..8172b595c9 100644 --- a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/howto/testing.adoc +++ b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/howto/testing.adoc @@ -45,3 +45,36 @@ include::{docs-java}/howto/testing/testcontainers/dynamicproperties/MyIntegratio ---- The above configuration allows Neo4j-related beans in the application to communicate with Neo4j running inside the Testcontainers-managed Docker container. + + + +[[howto.testing.slice-tests]] +=== Structure `@Configuration` classes for inclusion in slice tests +Slice tests work by restricting Spring Framework's component scanning to a limited set of components based on their type. +For any beans that are not created via component scanning, for example, beans that are created using the `@Bean` annotation, slice tests will not be able to include/exclude them from the application context. +Consider this example: + +[source,java,indent=0,subs="verbatim"] +---- +include::{docs-java}/howto/testing/slicetests/MyConfiguration.java[] +---- + +For a `@WebMvcTest` for an application with the above `@Configuration` class, you might expect to have the `SecurityFilterChain` bean in the application context so that you can test if your controller endpoints are secured properly. +However, `MyConfiguration` is not picked up by @WebMvcTest's component scanning filter because it doesn't match any of the types specified by the filter. +You can include the configuration explicitly by annotating the test class with `@Import(MySecurityConfiguration.class)`. +This will load all the beans in `MyConfiguration` including the `BasicDataSource` bean which isn't required when testing the web tier. +Splitting the configuration class into two will enable importing just the security configuration. + +[source,java,indent=0,subs="verbatim"] +---- +include::{docs-java}/howto/testing/slicetests/MySecurityConfiguration.java[] +---- + +[source,java,indent=0,subs="verbatim"] +---- +include::{docs-java}/howto/testing/slicetests/MyDatasourceConfiguration.java[] +---- + +Having a single configuration class can be inefficient when beans of a certain domain needed to be included in slice tests. +Instead, structuring the application's configuration as multiple granular classes with beans for a specific domain can enable importing them only for specific slice tests. + diff --git a/spring-boot-project/spring-boot-docs/src/main/java/org/springframework/boot/docs/howto/testing/slicetests/MyConfiguration.java b/spring-boot-project/spring-boot-docs/src/main/java/org/springframework/boot/docs/howto/testing/slicetests/MyConfiguration.java new file mode 100644 index 0000000000..e6d24d1bbe --- /dev/null +++ b/spring-boot-project/spring-boot-docs/src/main/java/org/springframework/boot/docs/howto/testing/slicetests/MyConfiguration.java @@ -0,0 +1,43 @@ +/* + * Copyright 2012-2022 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 + * + * https://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.docs.howto.testing.slicetests; + +import org.apache.commons.dbcp2.BasicDataSource; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.boot.jdbc.DataSourceBuilder; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.web.SecurityFilterChain; + +@Configuration(proxyBeanMethods = false) +public class MyConfiguration { + + @Bean + public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { + http.authorizeRequests().anyRequest().authenticated(); + return http.build(); + } + + @Bean + @ConfigurationProperties("app.datasource.second") + public BasicDataSource secondDataSource() { + return DataSourceBuilder.create().type(BasicDataSource.class).build(); + } + +} diff --git a/spring-boot-project/spring-boot-docs/src/main/java/org/springframework/boot/docs/howto/testing/slicetests/MyDatasourceConfiguration.java b/spring-boot-project/spring-boot-docs/src/main/java/org/springframework/boot/docs/howto/testing/slicetests/MyDatasourceConfiguration.java new file mode 100644 index 0000000000..70263ea82b --- /dev/null +++ b/spring-boot-project/spring-boot-docs/src/main/java/org/springframework/boot/docs/howto/testing/slicetests/MyDatasourceConfiguration.java @@ -0,0 +1,35 @@ +/* + * Copyright 2012-2022 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 + * + * https://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.docs.howto.testing.slicetests; + +import org.apache.commons.dbcp2.BasicDataSource; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.boot.jdbc.DataSourceBuilder; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration(proxyBeanMethods = false) +class MyDatasourceConfiguration { + + @Bean + @ConfigurationProperties("app.datasource.second") + public BasicDataSource secondDataSource() { + return DataSourceBuilder.create().type(BasicDataSource.class).build(); + } + +} diff --git a/spring-boot-project/spring-boot-docs/src/main/java/org/springframework/boot/docs/howto/testing/slicetests/MySecurityConfiguration.java b/spring-boot-project/spring-boot-docs/src/main/java/org/springframework/boot/docs/howto/testing/slicetests/MySecurityConfiguration.java new file mode 100644 index 0000000000..874d0bd668 --- /dev/null +++ b/spring-boot-project/spring-boot-docs/src/main/java/org/springframework/boot/docs/howto/testing/slicetests/MySecurityConfiguration.java @@ -0,0 +1,33 @@ +/* + * Copyright 2012-2022 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 + * + * https://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.docs.howto.testing.slicetests; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.web.SecurityFilterChain; + +@Configuration(proxyBeanMethods = false) +class MySecurityConfiguration { + + @Bean + public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { + http.authorizeRequests().anyRequest().authenticated(); + return http.build(); + } + +}