diff --git a/pom.xml b/pom.xml
index 5c427590d6..24a8ed6842 100644
--- a/pom.xml
+++ b/pom.xml
@@ -45,6 +45,7 @@
spring-cloud-gcp-bigquery
spring-cloud-gcp-security-firebase
spring-cloud-gcp-secretmanager
+ spring-cloud-gcp-test-support
diff --git a/spring-cloud-gcp-dependencies/pom.xml b/spring-cloud-gcp-dependencies/pom.xml
index af8cd4b64b..d74ae0eef7 100644
--- a/spring-cloud-gcp-dependencies/pom.xml
+++ b/spring-cloud-gcp-dependencies/pom.xml
@@ -78,6 +78,11 @@
spring-cloud-gcp-security-iap
${project.version}
+
+ org.springframework.cloud
+ spring-cloud-gcp-test-support
+ ${project.version}
+
org.springframework.cloud
spring-cloud-gcp-vision
diff --git a/spring-cloud-gcp-test-support/pom.xml b/spring-cloud-gcp-test-support/pom.xml
new file mode 100644
index 0000000000..1978c4d66e
--- /dev/null
+++ b/spring-cloud-gcp-test-support/pom.xml
@@ -0,0 +1,53 @@
+
+
+
+
+ spring-cloud-gcp
+ org.springframework.cloud
+ 1.2.4.BUILD-SNAPSHOT
+
+ 4.0.0
+
+ spring-cloud-gcp-test-support
+ Spring Cloud GCP Test Support
+ Spring Cloud GCP Test Support
+
+
+
+ org.springframework.boot
+ spring-boot
+
+
+ org.springframework.boot
+ spring-boot-test
+
+
+ org.springframework.boot
+ spring-boot-test-autoconfigure
+
+
+ org.springframework.cloud
+ spring-cloud-gcp-autoconfigure
+
+
+ org.springframework.cloud
+ spring-cloud-gcp-data-spanner
+ true
+
+
+
+ org.junit.jupiter
+ junit-jupiter-api
+ true
+
+
+
+ org.springframework
+ spring-test
+ true
+
+
+
+
diff --git a/spring-cloud-gcp-test-support/src/main/java/org/springframework/cloud/gcp/test/autoconfigure/data/spanner/AutoConfigureDataSpanner.java b/spring-cloud-gcp-test-support/src/main/java/org/springframework/cloud/gcp/test/autoconfigure/data/spanner/AutoConfigureDataSpanner.java
new file mode 100644
index 0000000000..e87b2385ea
--- /dev/null
+++ b/spring-cloud-gcp-test-support/src/main/java/org/springframework/cloud/gcp/test/autoconfigure/data/spanner/AutoConfigureDataSpanner.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2017-2020 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.cloud.gcp.test.autoconfigure.data.spanner;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
+
+/**
+ * @author Eddú Meléndez
+ */
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+@Inherited
+@ImportAutoConfiguration
+public @interface AutoConfigureDataSpanner {
+}
diff --git a/spring-cloud-gcp-test-support/src/main/java/org/springframework/cloud/gcp/test/autoconfigure/data/spanner/DataSpannerTest.java b/spring-cloud-gcp-test-support/src/main/java/org/springframework/cloud/gcp/test/autoconfigure/data/spanner/DataSpannerTest.java
new file mode 100644
index 0000000000..d4c6d6766c
--- /dev/null
+++ b/spring-cloud-gcp-test-support/src/main/java/org/springframework/cloud/gcp/test/autoconfigure/data/spanner/DataSpannerTest.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2017-2020 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.cloud.gcp.test.autoconfigure.data.spanner;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.test.autoconfigure.OverrideAutoConfiguration;
+import org.springframework.boot.test.autoconfigure.core.AutoConfigureCache;
+import org.springframework.boot.test.autoconfigure.filter.TypeExcludeFilters;
+import org.springframework.context.annotation.ComponentScan.Filter;
+import org.springframework.core.annotation.AliasFor;
+import org.springframework.core.env.Environment;
+import org.springframework.test.context.BootstrapWith;
+import org.springframework.transaction.annotation.Transactional;
+
+/**
+ * @author Eddú Meléndez
+ */
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+@Inherited
+@BootstrapWith(DataSpannerTestContextBootstrapper.class)
+@OverrideAutoConfiguration(enabled = false)
+@TypeExcludeFilters(DataSpannerTypeExcludeFilter.class)
+@Transactional
+@AutoConfigureCache
+@AutoConfigureDataSpanner
+@ImportAutoConfiguration
+public @interface DataSpannerTest {
+
+ /**
+ * Properties in form {@literal key=value} that should be added to the Spring
+ * {@link Environment} before the test runs.
+ * @return the properties to add
+ */
+ String[] properties() default {};
+
+ /**
+ * Determines if default filtering should be used with
+ * {@link SpringBootApplication @SpringBootApplication}. By default no beans are
+ * included.
+ * @see #includeFilters()
+ * @see #excludeFilters()
+ * @return if default filters should be used
+ */
+ boolean useDefaultFilters() default true;
+
+ /**
+ * A set of include filters which can be used to add otherwise filtered beans to the
+ * application context.
+ * @return include filters to apply
+ */
+ Filter[] includeFilters() default {};
+
+ /**
+ * A set of exclude filters which can be used to filter beans that would otherwise be
+ * added to the application context.
+ * @return exclude filters to apply
+ */
+ Filter[] excludeFilters() default {};
+
+ /**
+ * Auto-configuration exclusions that should be applied for this test.
+ * @return auto-configuration exclusions to apply
+ */
+ @AliasFor(annotation = ImportAutoConfiguration.class, attribute = "exclude")
+ Class>[] excludeAutoConfiguration() default {};
+
+}
diff --git a/spring-cloud-gcp-test-support/src/main/java/org/springframework/cloud/gcp/test/autoconfigure/data/spanner/DataSpannerTestContextBootstrapper.java b/spring-cloud-gcp-test-support/src/main/java/org/springframework/cloud/gcp/test/autoconfigure/data/spanner/DataSpannerTestContextBootstrapper.java
new file mode 100644
index 0000000000..67659bd1a6
--- /dev/null
+++ b/spring-cloud-gcp-test-support/src/main/java/org/springframework/cloud/gcp/test/autoconfigure/data/spanner/DataSpannerTestContextBootstrapper.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2017-2020 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.cloud.gcp.test.autoconfigure.data.spanner;
+
+import org.springframework.boot.test.context.SpringBootTestContextBootstrapper;
+import org.springframework.core.annotation.MergedAnnotations;
+import org.springframework.core.annotation.MergedAnnotations.SearchStrategy;
+
+/**
+ * @author Eddú Meléndez
+ */
+class DataSpannerTestContextBootstrapper extends SpringBootTestContextBootstrapper {
+
+ @Override
+ protected String[] getProperties(Class> testClass) {
+ return MergedAnnotations.from(testClass, SearchStrategy.INHERITED_ANNOTATIONS).get(DataSpannerTest.class)
+ .getValue("properties", String[].class).orElse(null);
+ }
+
+}
diff --git a/spring-cloud-gcp-test-support/src/main/java/org/springframework/cloud/gcp/test/autoconfigure/data/spanner/DataSpannerTypeExcludeFilter.java b/spring-cloud-gcp-test-support/src/main/java/org/springframework/cloud/gcp/test/autoconfigure/data/spanner/DataSpannerTypeExcludeFilter.java
new file mode 100644
index 0000000000..4a21a9134f
--- /dev/null
+++ b/spring-cloud-gcp-test-support/src/main/java/org/springframework/cloud/gcp/test/autoconfigure/data/spanner/DataSpannerTypeExcludeFilter.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2017-2020 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.cloud.gcp.test.autoconfigure.data.spanner;
+
+import org.springframework.boot.test.autoconfigure.filter.StandardAnnotationCustomizableTypeExcludeFilter;
+
+/**
+ * @author Eddú Meléndez
+ */
+public class DataSpannerTypeExcludeFilter extends StandardAnnotationCustomizableTypeExcludeFilter {
+
+ DataSpannerTypeExcludeFilter(Class> testClass) {
+ super(testClass);
+ }
+
+}
diff --git a/spring-cloud-gcp-test-support/src/main/java/org/springframework/cloud/gcp/test/autoconfigure/data/spanner/package-info.java b/spring-cloud-gcp-test-support/src/main/java/org/springframework/cloud/gcp/test/autoconfigure/data/spanner/package-info.java
new file mode 100644
index 0000000000..af78c07a2c
--- /dev/null
+++ b/spring-cloud-gcp-test-support/src/main/java/org/springframework/cloud/gcp/test/autoconfigure/data/spanner/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2017-2020 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.
+ */
+
+/**
+ * Auto-configuration for Data Spanner tests.
+ */
+package org.springframework.cloud.gcp.test.autoconfigure.data.spanner;
diff --git a/spring-cloud-gcp-test-support/src/main/resources/META-INF/spring.factories b/spring-cloud-gcp-test-support/src/main/resources/META-INF/spring.factories
new file mode 100644
index 0000000000..8e2978b239
--- /dev/null
+++ b/spring-cloud-gcp-test-support/src/main/resources/META-INF/spring.factories
@@ -0,0 +1,6 @@
+org.springframework.cloud.gcp.test.autoconfigure.data.spanner.AutoConfigureDataSpanner=\
+org.springframework.cloud.gcp.autoconfigure.core.GcpContextAutoConfiguration,\
+org.springframework.cloud.gcp.autoconfigure.spanner.GcpSpannerAutoConfiguration,\
+org.springframework.cloud.gcp.autoconfigure.spanner.GcpSpannerEmulatorAutoConfiguration,\
+org.springframework.cloud.gcp.autoconfigure.spanner.SpannerRepositoriesAutoConfiguration,\
+org.springframework.cloud.gcp.autoconfigure.spanner.SpannerTransactionManagerAutoConfiguration
diff --git a/spring-cloud-gcp-test-support/src/test/java/org/springframework/cloud/gcp/test/autoconfigure/data/spanner/DataSpannerTestIntegrationTests.java b/spring-cloud-gcp-test-support/src/test/java/org/springframework/cloud/gcp/test/autoconfigure/data/spanner/DataSpannerTestIntegrationTests.java
new file mode 100644
index 0000000000..b445dee369
--- /dev/null
+++ b/spring-cloud-gcp-test-support/src/test/java/org/springframework/cloud/gcp/test/autoconfigure/data/spanner/DataSpannerTestIntegrationTests.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2017-2020 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.cloud.gcp.test.autoconfigure.data.spanner;
+
+import com.google.api.gax.core.CredentialsProvider;
+import com.google.auth.Credentials;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.TestConfiguration;
+import org.springframework.context.annotation.Bean;
+import org.springframework.test.context.junit4.SpringRunner;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+
+@RunWith(SpringRunner.class)
+@DataSpannerTest(properties = {"spring.cloud.gcp.spanner.instance-id=test-instance",
+ "spring.cloud.gcp.spanner.database=test-database", "spring.cloud.gcp.spanner.emulator.enabled=true"})
+public class DataSpannerTestIntegrationTests {
+
+ @Autowired
+ private ExampleRepository exampleRepository;
+
+ @Test
+ public void test() {
+ ExampleEntity exampleEntity = new ExampleEntity(1L, "Java");
+ this.exampleRepository.save(exampleEntity);
+
+ Iterable entities = this.exampleRepository.findAll();
+ assertThat(entities).hasSize(1);
+ }
+
+ @TestConfiguration
+ static class TestConfig {
+
+ @Bean
+ public CredentialsProvider credentialsProvider() {
+ return () -> mock(Credentials.class);
+ }
+ }
+}
diff --git a/spring-cloud-gcp-test-support/src/test/java/org/springframework/cloud/gcp/test/autoconfigure/data/spanner/ExampleEntity.java b/spring-cloud-gcp-test-support/src/test/java/org/springframework/cloud/gcp/test/autoconfigure/data/spanner/ExampleEntity.java
new file mode 100644
index 0000000000..3f9c2a1a77
--- /dev/null
+++ b/spring-cloud-gcp-test-support/src/test/java/org/springframework/cloud/gcp/test/autoconfigure/data/spanner/ExampleEntity.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2017-2020 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.cloud.gcp.test.autoconfigure.data.spanner;
+
+import org.springframework.cloud.gcp.data.spanner.core.mapping.PrimaryKey;
+import org.springframework.cloud.gcp.data.spanner.core.mapping.Table;
+
+@Table(name = "TestTable")
+public class ExampleEntity {
+
+ @PrimaryKey
+ private Long key;
+
+ private String value;
+
+ public ExampleEntity(Long key, String value) {
+ this.key = key;
+ this.value = value;
+ }
+
+ public Long getKey() {
+ return key;
+ }
+
+ public void setKey(Long key) {
+ this.key = key;
+ }
+
+ public String getValue() {
+ return value;
+ }
+
+ public void setValue(String value) {
+ this.value = value;
+ }
+}
diff --git a/spring-cloud-gcp-test-support/src/test/java/org/springframework/cloud/gcp/test/autoconfigure/data/spanner/ExampleRepository.java b/spring-cloud-gcp-test-support/src/test/java/org/springframework/cloud/gcp/test/autoconfigure/data/spanner/ExampleRepository.java
new file mode 100644
index 0000000000..0a8735f953
--- /dev/null
+++ b/spring-cloud-gcp-test-support/src/test/java/org/springframework/cloud/gcp/test/autoconfigure/data/spanner/ExampleRepository.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2017-2020 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.cloud.gcp.test.autoconfigure.data.spanner;
+
+import org.springframework.cloud.gcp.data.spanner.repository.SpannerRepository;
+
+public interface ExampleRepository extends SpannerRepository {
+}
diff --git a/spring-cloud-gcp-test-support/src/test/java/org/springframework/cloud/gcp/test/autoconfigure/data/spanner/SpannerTestApplication.java b/spring-cloud-gcp-test-support/src/test/java/org/springframework/cloud/gcp/test/autoconfigure/data/spanner/SpannerTestApplication.java
new file mode 100644
index 0000000000..7a48da60de
--- /dev/null
+++ b/spring-cloud-gcp-test-support/src/test/java/org/springframework/cloud/gcp/test/autoconfigure/data/spanner/SpannerTestApplication.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2017-2020 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.cloud.gcp.test.autoconfigure.data.spanner;
+
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class SpannerTestApplication {
+}
diff --git a/spring-cloud-gcp-test-support/src/test/resources/application.properties b/spring-cloud-gcp-test-support/src/test/resources/application.properties
new file mode 100644
index 0000000000..d9894bf130
--- /dev/null
+++ b/spring-cloud-gcp-test-support/src/test/resources/application.properties
@@ -0,0 +1,17 @@
+#
+# Copyright 2017-2020 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.
+#
+
+spring.cloud.gcp.spanner.project-id=test-project