diff --git a/spring-test/src/main/java/org/springframework/test/context/ActiveProfiles.java b/spring-test/src/main/java/org/springframework/test/context/ActiveProfiles.java
index 39bfb5d3cd8..0b76fa42318 100644
--- a/spring-test/src/main/java/org/springframework/test/context/ActiveProfiles.java
+++ b/spring-test/src/main/java/org/springframework/test/context/ActiveProfiles.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-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.
@@ -34,6 +34,7 @@ import java.lang.annotation.Target;
* @see SmartContextLoader
* @see MergedContextConfiguration
* @see ContextConfiguration
+ * @see ActiveProfilesResolver
* @see org.springframework.context.ApplicationContext
* @see org.springframework.context.annotation.Profile
*/
@@ -47,8 +48,8 @@ public @interface ActiveProfiles {
* Alias for {@link #profiles}.
*
*
This attribute may not be used in conjunction
- * with {@link #profiles}, but it may be used instead of
- * {@link #profiles}.
+ * with {@link #profiles} or {@link #resolver}, but it may be used
+ * instead of them.
*/
String[] value() default {};
@@ -56,11 +57,24 @@ public @interface ActiveProfiles {
* The bean definition profiles to activate.
*
*
This attribute may not be used in conjunction
- * with {@link #value}, but it may be used instead of
- * {@link #value}.
+ * with {@link #value} or {@link #resolver}, but it may be used
+ * instead of them.
*/
String[] profiles() default {};
+ /**
+ * The type of {@link ActiveProfilesResolver} to use for resolving the active
+ * bean definition profiles programmatically.
+ *
+ *
This attribute may not be used in conjunction
+ * with {@link #profiles} or {@link #value}, but it may be used instead
+ * of them in order to resolve the active profiles programmatically.
+ *
+ * @since 4.0
+ * @see ActiveProfilesResolver
+ */
+ Class extends ActiveProfilesResolver> resolver() default ActiveProfilesResolver.class;
+
/**
* Whether or not bean definition profiles from superclasses should be
* inherited.
diff --git a/spring-test/src/main/java/org/springframework/test/context/ActiveProfilesResolver.java b/spring-test/src/main/java/org/springframework/test/context/ActiveProfilesResolver.java
new file mode 100644
index 00000000000..0985ebb02c3
--- /dev/null
+++ b/spring-test/src/main/java/org/springframework/test/context/ActiveProfilesResolver.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2002-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.test.context;
+
+/**
+ * Strategy interface for programmatically resolving which active bean
+ * definition profiles should be used when loading an
+ * {@link org.springframework.context.ApplicationContext ApplicationContext}
+ * for a test class.
+ *
+ *
A custom {@code ActiveProfilesResolver} can be registered via the
+ * {@link ActiveProfiles#resolver resolver} attribute of {@code @ActiveProfiles}.
+ *
+ *
Concrete implementations must provide a {@code public} no-args constructor.
+ *
+ * @author Sam Brannen
+ * @author Michail Nikolaev
+ * @since 4.0
+ * @see ActiveProfiles
+ */
+public interface ActiveProfilesResolver {
+
+ /**
+ * Resolve the bean definition profiles to use when loading an
+ * {@code ApplicationContext} for the given {@linkplain Class test class}.
+ *
+ * @param testClass the test class for which the profiles should be resolved;
+ * never {@code null}
+ * @return the list of bean definition profiles to use when loading the
+ * {@code ApplicationContext}; never {@code null}
+ * @see ActiveProfiles#resolver
+ * @see ActiveProfiles#inheritProfiles
+ */
+ String[] resolve(Class> testClass);
+
+}
diff --git a/spring-test/src/main/java/org/springframework/test/context/ContextLoaderUtils.java b/spring-test/src/main/java/org/springframework/test/context/ContextLoaderUtils.java
index a43897eb0b6..3fb2b0a6e7f 100644
--- a/spring-test/src/main/java/org/springframework/test/context/ContextLoaderUtils.java
+++ b/spring-test/src/main/java/org/springframework/test/context/ContextLoaderUtils.java
@@ -16,11 +16,6 @@
package org.springframework.test.context;
-import static org.springframework.beans.BeanUtils.instantiateClass;
-import static org.springframework.core.annotation.AnnotationUtils.findAnnotationDeclaringClass;
-import static org.springframework.core.annotation.AnnotationUtils.findAnnotationDeclaringClassForTypes;
-import static org.springframework.core.annotation.AnnotationUtils.isAnnotationDeclaredLocally;
-
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
@@ -42,6 +37,9 @@ import org.springframework.util.ClassUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
+import static org.springframework.beans.BeanUtils.*;
+import static org.springframework.core.annotation.AnnotationUtils.*;
+
/**
* Utility methods for working with {@link ContextLoader ContextLoaders} and
* {@link SmartContextLoader SmartContextLoaders} and resolving resource locations,
@@ -49,12 +47,14 @@ import org.springframework.util.StringUtils;
* initializers.
*
* @author Sam Brannen
+ * @author Michail Nikolaev
* @since 3.1
* @see ContextLoader
* @see SmartContextLoader
* @see ContextConfiguration
* @see ContextConfigurationAttributes
* @see ActiveProfiles
+ * @see ActiveProfilesResolver
* @see ApplicationContextInitializer
* @see ContextHierarchy
* @see MergedContextConfiguration
@@ -477,24 +477,43 @@ abstract class ContextLoaderUtils {
while (declaringClass != null) {
ActiveProfiles annotation = declaringClass.getAnnotation(annotationType);
-
if (logger.isTraceEnabled()) {
logger.trace(String.format("Retrieved @ActiveProfiles [%s] for declaring class [%s].", annotation,
declaringClass.getName()));
}
+ validateActiveProfilesConfiguration(declaringClass, annotation);
String[] profiles = annotation.profiles();
String[] valueProfiles = annotation.value();
+ Class extends ActiveProfilesResolver> resolverClass = annotation.resolver();
- if (!ObjectUtils.isEmpty(valueProfiles) && !ObjectUtils.isEmpty(profiles)) {
- String msg = String.format("Test class [%s] has been configured with @ActiveProfiles' 'value' [%s] "
- + "and 'profiles' [%s] attributes. Only one declaration of active bean "
- + "definition profiles is permitted per @ActiveProfiles annotation.", declaringClass.getName(),
- ObjectUtils.nullSafeToString(valueProfiles), ObjectUtils.nullSafeToString(profiles));
- logger.error(msg);
- throw new IllegalStateException(msg);
+ boolean resolverDeclared = !ActiveProfilesResolver.class.equals(resolverClass);
+ boolean valueDeclared = !ObjectUtils.isEmpty(valueProfiles);
+
+ if (resolverDeclared) {
+ ActiveProfilesResolver resolver = null;
+ try {
+ resolver = instantiateClass(resolverClass, ActiveProfilesResolver.class);
+ }
+ catch (Exception e) {
+ String msg = String.format("Could not instantiate ActiveProfilesResolver of "
+ + "type [%s] for test class [%s].", resolverClass.getName(), declaringClass.getName());
+ logger.error(msg);
+ throw new IllegalStateException(msg, e);
+ }
+
+ if (resolver != null) {
+ profiles = resolver.resolve(declaringClass);
+ if (profiles == null) {
+ String msg = String.format(
+ "ActiveProfilesResolver [%s] returned a null array of bean definition profiles.",
+ resolverClass.getName());
+ logger.error(msg);
+ throw new IllegalStateException(msg);
+ }
+ }
}
- else if (!ObjectUtils.isEmpty(valueProfiles)) {
+ else if (valueDeclared) {
profiles = valueProfiles;
}
@@ -511,6 +530,43 @@ abstract class ContextLoaderUtils {
return StringUtils.toStringArray(activeProfiles);
}
+ private static void validateActiveProfilesConfiguration(Class> declaringClass, ActiveProfiles annotation) {
+ String[] valueProfiles = annotation.value();
+ String[] profiles = annotation.profiles();
+ Class extends ActiveProfilesResolver> resolverClass = annotation.resolver();
+ boolean valueDeclared = !ObjectUtils.isEmpty(valueProfiles);
+ boolean profilesDeclared = !ObjectUtils.isEmpty(profiles);
+ boolean resolverDeclared = !ActiveProfilesResolver.class.equals(resolverClass);
+
+ String msg = null;
+
+ if (valueDeclared && profilesDeclared) {
+ msg = String.format("Test class [%s] has been configured with @ActiveProfiles' 'value' [%s] "
+ + "and 'profiles' [%s] attributes. Only one declaration of active bean "
+ + "definition profiles is permitted per @ActiveProfiles annotation.", declaringClass.getName(),
+ ObjectUtils.nullSafeToString(valueProfiles), ObjectUtils.nullSafeToString(profiles));
+ }
+ else if (valueDeclared && resolverDeclared) {
+ msg = String.format("Test class [%s] has been configured with @ActiveProfiles' 'value' [%s] "
+ + "and 'resolver' [%s] attributes. Only one source of active bean "
+ + "definition profiles is permitted per @ActiveProfiles annotation, "
+ + "either declaritively or programmatically.", declaringClass.getName(),
+ ObjectUtils.nullSafeToString(valueProfiles), resolverClass.getName());
+ }
+ else if (profilesDeclared && resolverDeclared) {
+ msg = String.format("Test class [%s] has been configured with @ActiveProfiles' 'profiles' [%s] "
+ + "and 'resolver' [%s] attributes. Only one source of active bean "
+ + "definition profiles is permitted per @ActiveProfiles annotation, "
+ + "either declaritively or programmatically.", declaringClass.getName(),
+ ObjectUtils.nullSafeToString(profiles), resolverClass.getName());
+ }
+
+ if (msg != null) {
+ logger.error(msg);
+ throw new IllegalStateException(msg);
+ }
+ }
+
/**
* Build the {@link MergedContextConfiguration merged context configuration} for
* the supplied {@link Class testClass} and {@code defaultContextLoaderClassName},
diff --git a/spring-test/src/test/java/org/springframework/test/context/ContextCacheTests.java b/spring-test/src/test/java/org/springframework/test/context/ContextCacheTests.java
index eb1c39a908b..21f15f441f4 100644
--- a/spring-test/src/test/java/org/springframework/test/context/ContextCacheTests.java
+++ b/spring-test/src/test/java/org/springframework/test/context/ContextCacheTests.java
@@ -32,6 +32,7 @@ import static org.springframework.test.context.SpringRunnerContextCacheTests.*;
* conjunction with cache keys used in {@link TestContext}.
*
* @author Sam Brannen
+ * @author Michail Nikolaev
* @since 3.1
* @see SpringRunnerContextCacheTests
*/
@@ -84,6 +85,7 @@ public class ContextCacheTests {
loadCtxAndAssertStats(FooBarProfilesTestCase.class, 1, 3, 1);
loadCtxAndAssertStats(FooBarProfilesTestCase.class, 1, 4, 1);
loadCtxAndAssertStats(BarFooProfilesTestCase.class, 1, 5, 1);
+ loadCtxAndAssertStats(FooBarActiveProfilesResolverTestCase.class, 1, 6, 1);
}
@Test
@@ -287,6 +289,19 @@ public class ContextCacheTests {
private static class BarFooProfilesTestCase {
}
+ private static class FooBarActiveProfilesResolver implements ActiveProfilesResolver {
+
+ @Override
+ public String[] resolve(Class> testClass) {
+ return new String[] { "foo", "bar" };
+ }
+ }
+
+ @ActiveProfiles(resolver = FooBarActiveProfilesResolver.class)
+ @ContextConfiguration(classes = Config.class, loader = AnnotationConfigContextLoader.class)
+ private static class FooBarActiveProfilesResolverTestCase {
+ }
+
@ContextHierarchy({ @ContextConfiguration })
private static class ClassHierarchyContextHierarchyLevel1TestCase {
diff --git a/spring-test/src/test/java/org/springframework/test/context/ContextLoaderUtilsTests.java b/spring-test/src/test/java/org/springframework/test/context/ContextLoaderUtilsTests.java
index ecedda4ddf9..6f032545b61 100644
--- a/spring-test/src/test/java/org/springframework/test/context/ContextLoaderUtilsTests.java
+++ b/spring-test/src/test/java/org/springframework/test/context/ContextLoaderUtilsTests.java
@@ -16,10 +16,6 @@
package org.springframework.test.context;
-import static org.hamcrest.CoreMatchers.*;
-import static org.junit.Assert.*;
-import static org.springframework.test.context.ContextLoaderUtils.*;
-
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
@@ -38,10 +34,15 @@ import org.springframework.test.context.support.DelegatingSmartContextLoader;
import org.springframework.test.context.support.GenericPropertiesContextLoader;
import org.springframework.web.context.support.GenericWebApplicationContext;
+import static org.hamcrest.CoreMatchers.*;
+import static org.junit.Assert.*;
+import static org.springframework.test.context.ContextLoaderUtils.*;
+
/**
* Unit tests for {@link ContextLoaderUtils}.
*
* @author Sam Brannen
+ * @author Michail Nikolaev
* @since 3.1
*/
public class ContextLoaderUtilsTests {
@@ -606,8 +607,94 @@ public class ContextLoaderUtilsTests {
assertTrue(list.contains("cat"));
}
+ /**
+ * @since 4.0
+ */
+ @Test
+ public void resolveActiveProfilesWithResolver() {
+ String[] profiles = resolveActiveProfiles(FooActiveProfilesResolverTest.class);
+ assertNotNull(profiles);
+ assertEquals(1, profiles.length);
+ assertArrayEquals(new String[] { "foo" }, profiles);
+ }
+
+ /**
+ * @since 4.0
+ */
+ @Test
+ public void resolveActiveProfilesWithInheritedResolver() {
+ String[] profiles = resolveActiveProfiles(InheritedFooActiveProfilesResolverTest.class);
+ assertNotNull(profiles);
+ assertEquals(1, profiles.length);
+ assertArrayEquals(new String[] { "foo" }, profiles);
+ }
+
+ /**
+ * @since 4.0
+ */
+ @Test
+ public void resolveActiveProfilesWithMergedInheritedResolver() {
+ String[] profiles = resolveActiveProfiles(MergedInheritedFooActiveProfilesResolverTest.class);
+ assertNotNull(profiles);
+ assertEquals(2, profiles.length);
+ List list = Arrays.asList(profiles);
+ assertTrue(list.contains("foo"));
+ assertTrue(list.contains("bar"));
+ }
+
+ /**
+ * @since 4.0
+ */
+ @Test
+ public void resolveActiveProfilesWithOverridenInheritedResolver() {
+ String[] profiles = resolveActiveProfiles(OverridenInheritedFooActiveProfilesResolverTest.class);
+ assertNotNull(profiles);
+ assertEquals(1, profiles.length);
+ assertArrayEquals(new String[] { "bar" }, profiles);
+ }
+
+ /**
+ * @since 4.0
+ */
+ @Test(expected = IllegalStateException.class)
+ public void resolveActiveProfilesWithConflictingResolverAndProfiles() {
+ resolveActiveProfiles(ConflictingResolverAndProfilesTest.class);
+ }
+
+ /**
+ * @since 4.0
+ */
+ @Test(expected = IllegalStateException.class)
+ public void resolveActiveProfilesWithConflictingResolverAndValue() {
+ resolveActiveProfiles(ConflictingResolverAndValueTest.class);
+ }
+
+ /**
+ * @since 4.0
+ */
+ @Test(expected = IllegalStateException.class)
+ public void resolveActiveProfilesWithConflictingProfilesAndValue() {
+ resolveActiveProfiles(ConflictingProfilesAndValueTest.class);
+ }
+
+ /**
+ * @since 4.0
+ */
+ @Test(expected = IllegalStateException.class)
+ public void resolveActiveProfilesWithResolverWithoutDefaultConstructor() {
+ resolveActiveProfiles(NoDefaultConstructorActiveProfilesResolverTest.class);
+ }
+
+ /**
+ * @since 4.0
+ */
+ @Test(expected = IllegalStateException.class)
+ public void resolveActiveProfilesWithResolverThatReturnsNull() {
+ resolveActiveProfiles(NullActiveProfilesResolverTest.class);
+ }
+
- // -------------------------------------------------------------------------
+ // --- General Purpose Classes and Config ----------------------------------
private static class Enigma {
}
@@ -677,6 +764,80 @@ public class ContextLoaderUtilsTests {
private static class Animals extends LocationsBar {
}
+ // --- ActiveProfilesResolver ----------------------------------------------
+
+ public static class FooActiveProfilesResolver implements ActiveProfilesResolver {
+
+ @Override
+ public String[] resolve(Class> testClass) {
+ return new String[] { "foo" };
+ }
+ }
+
+ public static class BarActiveProfilesResolver implements ActiveProfilesResolver {
+
+ @Override
+ public String[] resolve(Class> testClass) {
+ return new String[] { "bar" };
+ }
+ }
+
+ public static class NullActiveProfilesResolver implements ActiveProfilesResolver {
+
+ @Override
+ public String[] resolve(Class> testClass) {
+ return null;
+ }
+ }
+
+ public static class NoDefaultConstructorActiveProfilesResolver implements ActiveProfilesResolver {
+
+ public NoDefaultConstructorActiveProfilesResolver(Object agument) {
+ }
+
+ @Override
+ public String[] resolve(Class> testClass) {
+ return null;
+ }
+ }
+
+ @ActiveProfiles(resolver = NullActiveProfilesResolver.class)
+ private static class NullActiveProfilesResolverTest {
+ }
+
+ @ActiveProfiles(resolver = NoDefaultConstructorActiveProfilesResolver.class)
+ private static class NoDefaultConstructorActiveProfilesResolverTest {
+ }
+
+ @ActiveProfiles(resolver = FooActiveProfilesResolver.class)
+ private static class FooActiveProfilesResolverTest {
+ }
+
+ private static class InheritedFooActiveProfilesResolverTest extends FooActiveProfilesResolverTest {
+ }
+
+ @ActiveProfiles(resolver = BarActiveProfilesResolver.class)
+ private static class MergedInheritedFooActiveProfilesResolverTest extends InheritedFooActiveProfilesResolverTest {
+ }
+
+ @ActiveProfiles(resolver = BarActiveProfilesResolver.class, inheritProfiles = false)
+ private static class OverridenInheritedFooActiveProfilesResolverTest extends InheritedFooActiveProfilesResolverTest {
+ }
+
+ @ActiveProfiles(resolver = BarActiveProfilesResolver.class, profiles = "conflict")
+ private static class ConflictingResolverAndProfilesTest {
+ }
+
+ @ActiveProfiles(resolver = BarActiveProfilesResolver.class, value = "conflict")
+ private static class ConflictingResolverAndValueTest {
+ }
+
+ @ActiveProfiles(profiles = "conflict", value = "conflict")
+ private static class ConflictingProfilesAndValueTest {
+ }
+
+ // --- ApplicationContextInitializer ---------------------------------------
+
private static class FooInitializer implements ApplicationContextInitializer {
@Override
@@ -707,6 +868,8 @@ public class ContextLoaderUtilsTests {
private static class OverriddenInitializersAndClassesBar extends InitializersFoo {
}
+ // --- @ContextHierarchy ---------------------------------------------------
+
@ContextConfiguration("foo.xml")
@ContextHierarchy(@ContextConfiguration("bar.xml"))
private static class SingleTestClassWithContextConfigurationAndContextHierarchy {
diff --git a/spring-test/src/test/java/org/springframework/test/context/junit4/SpringJUnit4TestSuite.java b/spring-test/src/test/java/org/springframework/test/context/junit4/SpringJUnit4TestSuite.java
index 72ddd1b8c4f..181bc8f7cb8 100644
--- a/spring-test/src/test/java/org/springframework/test/context/junit4/SpringJUnit4TestSuite.java
+++ b/spring-test/src/test/java/org/springframework/test/context/junit4/SpringJUnit4TestSuite.java
@@ -37,7 +37,9 @@ import org.springframework.test.context.junit4.annotation.ExplicitConfigClassesI
import org.springframework.test.context.junit4.orm.HibernateSessionFlushingTests;
import org.springframework.test.context.junit4.profile.annotation.DefaultProfileAnnotationConfigTests;
import org.springframework.test.context.junit4.profile.annotation.DevProfileAnnotationConfigTests;
+import org.springframework.test.context.junit4.profile.annotation.DevProfileResolverAnnotationConfigTests;
import org.springframework.test.context.junit4.profile.xml.DefaultProfileXmlConfigTests;
+import org.springframework.test.context.junit4.profile.xml.DevProfileResolverXmlConfigTests;
import org.springframework.test.context.junit4.profile.xml.DevProfileXmlConfigTests;
/**
@@ -77,8 +79,10 @@ StandardJUnit4FeaturesTests.class,//
DefaultLoaderBeanOverridingExplicitConfigClassesInheritedTests.class,//
DefaultProfileAnnotationConfigTests.class,//
DevProfileAnnotationConfigTests.class,//
+ DevProfileResolverAnnotationConfigTests.class,//
DefaultProfileXmlConfigTests.class,//
DevProfileXmlConfigTests.class,//
+ DevProfileResolverXmlConfigTests.class,//
ExpectedExceptionSpringRunnerTests.class,//
TimedSpringRunnerTests.class,//
RepeatedSpringRunnerTests.class,//
diff --git a/spring-test/src/test/java/org/springframework/test/context/junit4/profile/annotation/DevProfileResolverAnnotationConfigTests.java b/spring-test/src/test/java/org/springframework/test/context/junit4/profile/annotation/DevProfileResolverAnnotationConfigTests.java
new file mode 100644
index 00000000000..09a4bca53aa
--- /dev/null
+++ b/spring-test/src/test/java/org/springframework/test/context/junit4/profile/annotation/DevProfileResolverAnnotationConfigTests.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2002-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.test.context.junit4.profile.annotation;
+
+import org.springframework.test.context.ActiveProfiles;
+import org.springframework.test.context.ActiveProfilesResolver;
+
+/**
+ * @author Michail Nikolaev
+ * @since 4.0
+ */
+@ActiveProfiles(resolver = DevProfileResolverAnnotationConfigTests.class, inheritProfiles = false)
+public class DevProfileResolverAnnotationConfigTests extends DevProfileAnnotationConfigTests implements
+ ActiveProfilesResolver {
+
+ @Override
+ public String[] resolve(Class> testClass) {
+ return new String[] { "dev" };
+ }
+}
diff --git a/spring-test/src/test/java/org/springframework/test/context/junit4/profile/annotation/ProfileAnnotationConfigTestSuite.java b/spring-test/src/test/java/org/springframework/test/context/junit4/profile/annotation/ProfileAnnotationConfigTestSuite.java
index b3b7068c39a..d0286917d83 100644
--- a/spring-test/src/test/java/org/springframework/test/context/junit4/profile/annotation/ProfileAnnotationConfigTestSuite.java
+++ b/spring-test/src/test/java/org/springframework/test/context/junit4/profile/annotation/ProfileAnnotationConfigTestSuite.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-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.
@@ -31,7 +31,8 @@ import org.junit.runners.Suite.SuiteClasses;
// Note: the following 'multi-line' layout is for enhanced code readability.
@SuiteClasses({//
DefaultProfileAnnotationConfigTests.class,//
- DevProfileAnnotationConfigTests.class //
+ DevProfileAnnotationConfigTests.class,//
+ DevProfileResolverAnnotationConfigTests.class //
})
public class ProfileAnnotationConfigTestSuite {
}
diff --git a/spring-test/src/test/java/org/springframework/test/context/junit4/profile/importresource/DevProfileResolverAnnotationConfigTests.java b/spring-test/src/test/java/org/springframework/test/context/junit4/profile/importresource/DevProfileResolverAnnotationConfigTests.java
new file mode 100644
index 00000000000..2cfafabe379
--- /dev/null
+++ b/spring-test/src/test/java/org/springframework/test/context/junit4/profile/importresource/DevProfileResolverAnnotationConfigTests.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2002-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.test.context.junit4.profile.importresource;
+
+import org.springframework.test.context.ActiveProfiles;
+import org.springframework.test.context.ActiveProfilesResolver;
+
+/**
+ * @author Michail Nikolaev
+ * @since 4.0
+ */
+@ActiveProfiles(resolver = DevProfileResolverAnnotationConfigTests.class, inheritProfiles = false)
+public class DevProfileResolverAnnotationConfigTests extends DevProfileAnnotationConfigTests implements
+ ActiveProfilesResolver {
+
+ @Override
+ public String[] resolve(Class> testClass) {
+ return new String[] { "dev" };
+ }
+}
diff --git a/spring-test/src/test/java/org/springframework/test/context/junit4/profile/resolver/ClassNameActiveProfilesResolver.java b/spring-test/src/test/java/org/springframework/test/context/junit4/profile/resolver/ClassNameActiveProfilesResolver.java
new file mode 100644
index 00000000000..51d5fc4c5d2
--- /dev/null
+++ b/spring-test/src/test/java/org/springframework/test/context/junit4/profile/resolver/ClassNameActiveProfilesResolver.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2002-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.test.context.junit4.profile.resolver;
+
+import org.springframework.test.context.ActiveProfilesResolver;
+
+/**
+ * @author Michail Nikolaev
+ * @since 4.0
+ */
+public class ClassNameActiveProfilesResolver implements ActiveProfilesResolver {
+
+ @Override
+ public String[] resolve(Class> testClass) {
+ return new String[] { testClass.getSimpleName().toLowerCase() };
+ }
+}
diff --git a/spring-test/src/test/java/org/springframework/test/context/junit4/profile/resolver/ClassNameActiveProfilesResolverTest.java b/spring-test/src/test/java/org/springframework/test/context/junit4/profile/resolver/ClassNameActiveProfilesResolverTest.java
new file mode 100644
index 00000000000..52e705250df
--- /dev/null
+++ b/spring-test/src/test/java/org/springframework/test/context/junit4/profile/resolver/ClassNameActiveProfilesResolverTest.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2002-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.test.context.junit4.profile.resolver;
+
+import java.util.Arrays;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.test.context.ActiveProfiles;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+
+import static org.junit.Assert.*;
+
+/**
+ * @author Michail Nikolaev
+ * @since 4.0
+ */
+@RunWith(SpringJUnit4ClassRunner.class)
+@ContextConfiguration
+@ActiveProfiles(resolver = ClassNameActiveProfilesResolver.class)
+public class ClassNameActiveProfilesResolverTest {
+
+ @Configuration
+ static class Config {
+
+ }
+
+
+ @Autowired
+ private ApplicationContext applicationContext;
+
+
+ @Test
+ public void test() {
+ assertTrue(Arrays.asList(applicationContext.getEnvironment().getActiveProfiles()).contains(
+ getClass().getSimpleName().toLowerCase()));
+ }
+
+}
diff --git a/spring-test/src/test/java/org/springframework/test/context/junit4/profile/xml/DevProfileResolverXmlConfigTests.java b/spring-test/src/test/java/org/springframework/test/context/junit4/profile/xml/DevProfileResolverXmlConfigTests.java
new file mode 100644
index 00000000000..36c3fdcc075
--- /dev/null
+++ b/spring-test/src/test/java/org/springframework/test/context/junit4/profile/xml/DevProfileResolverXmlConfigTests.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2002-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.test.context.junit4.profile.xml;
+
+import org.springframework.test.context.ActiveProfiles;
+import org.springframework.test.context.ActiveProfilesResolver;
+
+/**
+ * @author Michail Nikolaev
+ * @since 4.0
+ */
+@ActiveProfiles(resolver = DevProfileResolverXmlConfigTests.class, inheritProfiles = false)
+public class DevProfileResolverXmlConfigTests extends DevProfileXmlConfigTests implements ActiveProfilesResolver {
+
+ @Override
+ public String[] resolve(Class> testClass) {
+ return new String[] { "dev" };
+ }
+
+}
diff --git a/spring-test/src/test/java/org/springframework/test/context/junit4/profile/xml/ProfileXmlConfigTestSuite.java b/spring-test/src/test/java/org/springframework/test/context/junit4/profile/xml/ProfileXmlConfigTestSuite.java
index 38a5939c617..16230c0a554 100644
--- a/spring-test/src/test/java/org/springframework/test/context/junit4/profile/xml/ProfileXmlConfigTestSuite.java
+++ b/spring-test/src/test/java/org/springframework/test/context/junit4/profile/xml/ProfileXmlConfigTestSuite.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2011 the original author or authors.
+ * Copyright 2002-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.
@@ -31,7 +31,8 @@ import org.junit.runners.Suite.SuiteClasses;
// Note: the following 'multi-line' layout is for enhanced code readability.
@SuiteClasses({//
DefaultProfileXmlConfigTests.class,//
- DevProfileXmlConfigTests.class //
+ DevProfileXmlConfigTests.class,//
+ DevProfileResolverXmlConfigTests.class //
})
public class ProfileXmlConfigTestSuite {
}
diff --git a/src/reference/docbook/testing.xml b/src/reference/docbook/testing.xml
index 72bb4d3c8f4..ec78308b388 100644
--- a/src/reference/docbook/testing.xml
+++ b/src/reference/docbook/testing.xml
@@ -197,7 +197,6 @@
TestContext framework is agnostic of the actual testing framework in use,
thus allowing instrumentation of tests in various environments including
JUnit, TestNG, and so on.
-
@@ -444,13 +443,13 @@
@ContextConfiguration("/test-config.xml")
public class XmlApplicationContextTests {
- // class body...
+ // class body...
}
@ContextConfiguration(classes = TestConfig.class)
public class ConfigClassApplicationContextTests {
- // class body...
+ // class body...
}
As an alternative or in addition to declaring resource
@@ -462,7 +461,7 @@ public class ConfigClassApplicationContextTests {
@ContextConfiguration(initializers = CustomContextIntializer.class)
public class ContextInitializerTests {
- // class body...
+ // class body...
}
@ContextConfiguration may
@@ -477,7 +476,7 @@ public class ContextInitializerTests {
role="bold">locations = "/test-context.xml", loader = CustomContextLoader.class)
public class CustomLoaderXmlApplicationContextTests {
- // class body...
+ // class body...
}
@@ -514,7 +513,7 @@ public class CustomLoaderXmlApplicationContextTests {
@ContextConfiguration
@WebAppConfiguration
public class WebAppTests {
- // class body...
+ // class body...
}
To override the default, specify a different base resource
@@ -527,7 +526,7 @@ public class WebAppTests {
@ContextConfiguration
@WebAppConfiguration("classpath:test-web-resources")
public class WebAppTests {
- // class body...
+ // class body...
}
Note that @WebAppConfiguration
@@ -559,7 +558,7 @@ public class WebAppTests {
@ContextConfiguration("/child-config.xml")
})
public class ContextHierarchyTests {
- // class body...
+ // class body...
}
@WebAppConfiguration
@@ -568,7 +567,7 @@ public class ContextHierarchyTests {
@ContextConfiguration(classes = WebConfig.class)
})
public class WebIntegrationTests {
- // class body...
+ // class body...
}
If you need to merge or override the configuration for a given
@@ -594,19 +593,24 @@ public class WebIntegrationTests {
@ContextConfiguration
@ActiveProfiles("dev")
public class DeveloperTests {
- // class body...
+ // class body...
}
@ContextConfiguration
@ActiveProfiles({"dev", "integration"})
public class DeveloperIntegrationTests {
- // class body...
+ // class body...
}
@ActiveProfiles provides
support for inheriting active bean definition
- profiles declared by superclasses by default.
+ profiles declared by superclasses by default. It is also possible
+ to resolve active bean definition profiles programmatically by
+ implementing a custom
+ ActiveProfilesResolver and
+ registering it via the resolver attribute of
+ @ActiveProfiles.
See
@@ -649,19 +653,20 @@ public class DeveloperIntegrationTests {
@DirtiesContext
public class ContextDirtyingTests {
- // some tests that result in the Spring container being dirtied
+ // some tests that result in the Spring container being dirtied
}
After each test method in the current test class, when
declared on a class with class mode set to
- AFTER_EACH_TEST_METHOD.@DirtiesContext(classMode = ClassMode.AFTER_EACH_TEST_METHOD)
+ AFTER_EACH_TEST_METHOD.
+
+ @DirtiesContext(classMode = ClassMode.AFTER_EACH_TEST_METHOD)
public class ContextDirtyingTests {
- // some tests that result in the Spring container being dirtied
-}
+ // some tests that result in the Spring container being dirtied
+}
@@ -670,7 +675,7 @@ public class ContextDirtyingTests {
@DirtiesContext
@Test
public void testProcessWhichDirtiesAppCtx() {
- // some logic that results in the Spring container being dirtied
+ // some logic that results in the Spring container being dirtied
}
@@ -696,7 +701,7 @@ public void testProcessWhichDirtiesAppCtx() {
@ContextConfiguration("/child-config.xml")
})
public class BaseTests {
- // class body...
+ // class body...
}
public class ExtendedTests extends BaseTests {
@@ -704,7 +709,7 @@ public class ExtendedTests extends BaseTests {
@Test
@DirtiesContext(hierarchyMode = HierarchyMode.CURRENT_LEVEL)
public void test() {
- // some logic that results in the child context being dirtied
+ // some logic that results in the child context being dirtied
}
}
@@ -729,7 +734,7 @@ public class ExtendedTests extends BaseTests {
@ContextConfiguration
@TestExecutionListeners({CustomTestExecutionListener.class, AnotherTestExecutionListener.class})
public class CustomTestExecutionListenerTests {
- // class body...
+ // class body...
}
@TestExecutionListeners
@@ -761,7 +766,7 @@ public class CustomTestExecutionListenerTests {
@TransactionConfiguration(transactionManager = "txMgr", defaultRollback = false)
public class CustomConfiguredTransactionalTests {
- // class body...
+ // class body...
}
@@ -793,7 +798,7 @@ public class CustomConfiguredTransactionalTests {
@Rollback(false)
@Test
public void testProcessWithoutRollback() {
- // ...
+ // ...
}
@@ -809,7 +814,7 @@ public void testProcessWithoutRollback() {
@BeforeTransaction
public void beforeTransaction() {
- // logic to be executed before a transaction is started
+ // logic to be executed before a transaction is started
}
@@ -825,7 +830,7 @@ public void testProcessWithoutRollback() {
@AfterTransaction
public void afterTransaction() {
- // logic to be executed after a transaction has ended
+ // logic to be executed after a transaction has ended
}
@@ -944,7 +949,7 @@ public void testProcessWithoutRollback() {
role="bold">name="java.vendor", value="Sun Microsystems Inc.")
@Test
public void testProcessWhichRunsOnlyOnSunJvm() {
- // some logic that should run only on Java VMs from Sun Microsystems
+ // some logic that should run only on Java VMs from Sun Microsystems
}
Alternatively, you can configure
@@ -957,7 +962,7 @@ public void testProcessWhichRunsOnlyOnSunJvm() {
role="bold">name="test-groups", values={"unit-tests", "integration-tests"})
@Test
public void testProcessWhichRunsForUnitOrIntegrationTestGroups() {
- // some logic that should run only for unit and integration test groups
+ // some logic that should run only for unit and integration test groups
}
@@ -977,7 +982,7 @@ public void testProcessWhichRunsForUnitOrIntegrationTestGroups() {
@ProfileValueSourceConfiguration(CustomProfileValueSource.class)
public class CustomProfileValueSourceTests {
- // class body...
+ // class body...
}
@@ -997,7 +1002,7 @@ public class CustomProfileValueSourceTests {
@Timed(millis=1000)
public void testProcessWithOneSecondTimeout() {
- // some logic that should not take longer than 1 second to execute
+ // some logic that should not take longer than 1 second to execute
}
Spring's @Timed annotation has
@@ -1030,7 +1035,7 @@ public void testProcessWithOneSecondTimeout() {
@Repeat(10)
@Test
public void testProcessRepeatedly() {
- // ...
+ // ...
}
@@ -1286,7 +1291,7 @@ public class MyTest {
@Autowired
private ApplicationContext applicationContext;
- // class body...
+ // class body...
}
Similarly, if your test is configured to load a
@@ -1301,7 +1306,7 @@ public class MyWebAppTest {
@Autowired
private WebApplicationContext wac;
- // class body...
+ // class body...
}
Dependency injection via
@@ -1355,11 +1360,11 @@ public class MyWebAppTest {
is.
@RunWith(SpringJUnit4ClassRunner.class)
-// ApplicationContext will be loaded from "/app-config.xml" and
-// "/test-config.xml" in the root of the classpath
+// ApplicationContext will be loaded from "/app-config.xml" and
+// "/test-config.xml" in the root of the classpath
@ContextConfiguration(locations={"/app-config.xml", "/test-config.xml"})
public class MyTest {
- // class body...
+ // class body...
}
@ContextConfiguration supports an
@@ -1374,7 +1379,7 @@ public class MyTest {
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({"/app-config.xml", "/test-config.xml"})
public class MyTest {
- // class body...
+ // class body...
}
If you omit both the locations and
@@ -1391,11 +1396,11 @@ public class MyTest {
package com.example;
@RunWith(SpringJUnit4ClassRunner.class)
-// ApplicationContext will be loaded from
-// "classpath:/com/example/MyTest-context.xml"
+// ApplicationContext will be loaded from
+// "classpath:/com/example/MyTest-context.xml"
@ContextConfiguration
public class MyTest {
- // class body...
+ // class body...
}
@@ -1410,10 +1415,10 @@ public class MyTest {
references to annotated classes.
@RunWith(SpringJUnit4ClassRunner.class)
-// ApplicationContext will be loaded from AppConfig and TestConfig
+// ApplicationContext will be loaded from AppConfig and TestConfig
@ContextConfiguration(classes = {AppConfig.class, TestConfig.class})
public class MyTest {
- // class body...
+ // class body...
}
If you omit the classes attribute from the
@@ -1433,19 +1438,19 @@ public class MyTest {
configuration class if desired.
@RunWith(SpringJUnit4ClassRunner.class)
-// ApplicationContext will be loaded from the
-// static inner Config class
+// ApplicationContext will be loaded from the
+// static inner Config class
@ContextConfiguration
public class OrderServiceTest {
@Configuration
static class Config {
- // this bean will be injected into the OrderServiceTest class
+ // this bean will be injected into the OrderServiceTest class
@Bean
public OrderService orderService() {
OrderService orderService = new OrderServiceImpl();
- // set properties, etc.
+ // set properties, etc.
return orderService;
}
}
@@ -1455,7 +1460,7 @@ public class OrderServiceTest {
@Test
public void testOrderService() {
- // test the orderService
+ // test the orderService
}
}
@@ -1520,13 +1525,13 @@ public class OrderServiceTest {
Spring's @Order annotation.
@RunWith(SpringJUnit4ClassRunner.class)
-// ApplicationContext will be loaded from TestConfig
-// and initialized by TestAppCtxInitializer
+// ApplicationContext will be loaded from TestConfig
+// and initialized by TestAppCtxInitializer
@ContextConfiguration(
classes = TestConfig.class,
initializers = TestAppCtxInitializer.class)
public class MyTest {
- // class body...
+ // class body...
}
It is also possible to omit the declaration of XML configuration
@@ -1539,11 +1544,11 @@ public class MyTest {
or configuration classes.
@RunWith(SpringJUnit4ClassRunner.class)
-// ApplicationContext will be initialized by EntireAppInitializer
-// which presumably registers beans in the context
+// ApplicationContext will be initialized by EntireAppInitializer
+// which presumably registers beans in the context
@ContextConfiguration(initializers = EntireAppInitializer.class)
public class MyTest {
- // class body...
+ // class body...
}
@@ -1585,18 +1590,18 @@ public class MyTest {
"base-config.xml".
@RunWith(SpringJUnit4ClassRunner.class)
-// ApplicationContext will be loaded from "/base-config.xml"
-// in the root of the classpath
+// ApplicationContext will be loaded from "/base-config.xml"
+// in the root of the classpath
@ContextConfiguration("/base-config.xml")
public class BaseTest {
- // class body...
+ // class body...
}
-// ApplicationContext will be loaded from "/base-config.xml" and
-// "/extended-config.xml" in the root of the classpath
+// ApplicationContext will be loaded from "/base-config.xml" and
+// "/extended-config.xml" in the root of the classpath
@ContextConfiguration("/extended-config.xml")
public class ExtendedTest extends BaseTest {
- // class body...
+ // class body...
}
Similarly, in the following example that uses annotated classes,
@@ -1609,16 +1614,16 @@ public class ExtendedTest extends BaseTest {
BaseConfig.
@RunWith(SpringJUnit4ClassRunner.class)
-// ApplicationContext will be loaded from BaseConfig
+// ApplicationContext will be loaded from BaseConfig
@ContextConfiguration(classes = BaseConfig.class)
public class BaseTest {
- // class body...
+ // class body...
}
-// ApplicationContext will be loaded from BaseConfig and ExtendedConfig
+// ApplicationContext will be loaded from BaseConfig and ExtendedConfig
@ContextConfiguration(classes = ExtendedConfig.class)
public class ExtendedTest extends BaseTest {
- // class body...
+ // class body...
}
In the following example that uses context initializers, the
@@ -1632,17 +1637,17 @@ public class ExtendedTest extends BaseTest {
Spring's @Order annotation.
@RunWith(SpringJUnit4ClassRunner.class)
-// ApplicationContext will be initialized by BaseInitializer
+// ApplicationContext will be initialized by BaseInitializer
@ContextConfiguration(initializers=BaseInitializer.class)
public class BaseTest {
- // class body...
+ // class body...
}
-// ApplicationContext will be initialized by BaseInitializer
-// and ExtendedInitializer
+// ApplicationContext will be initialized by BaseInitializer
+// and ExtendedInitializer
@ContextConfiguration(initializers=ExtendedInitializer.class)
public class ExtendedTest extends BaseTest {
- // class body...
+ // class body...
}
@@ -1814,7 +1819,7 @@ public class TransferServiceTest {
@Test
public void testTransferService() {
- // test the transferService
+ // test the transferService
}
}
@@ -1849,7 +1854,113 @@ public class TransferServiceTest {
@ContextConfiguration annotation. The
body of the test class itself remains completely unchanged.
-
+ It is often the case that a single set of profiles is used
+ across multiple test classes within a given project. Thus, to avoid
+ duplicate declarations of the
+ @ActiveProfiles annotation it is
+ possible to declare @ActiveProfiles
+ once on a base class, and subclasses will automatically inherit the
+ @ActiveProfiles configuration from the
+ base class. In the following example, the declaration of
+ @ActiveProfiles (as well as other
+ annotations) has been moved to an abstract superclass,
+ AbstractIntegrationTest.
+
+ package com.bank.service;
+
+@RunWith(SpringJUnit4ClassRunner.class)
+@ContextConfiguration(
+ classes = {
+ TransferServiceConfig.class,
+ StandaloneDataConfig.class,
+ JndiDataConfig.class})
+@ActiveProfiles("dev")
+public abstract class AbstractIntegrationTest {
+}
+
+ package com.bank.service;
+
+// "dev" profile inherited from superclass
+public class TransferServiceTest extends AbstractIntegrationTest {
+
+ @Autowired
+ private TransferService transferService;
+
+ @Test
+ public void testTransferService() {
+ // test the transferService
+ }
+}
+
+ @ActiveProfiles also supports an
+ inheritProfiles attribute that can be used to
+ disable the inheritance of active profiles.
+
+ package com.bank.service;
+
+// "dev" profile overridden with "production"
+@ActiveProfiles(profiles = "production", inheritProfiles = false)
+public class ProductionTransferServiceTest extends AbstractIntegrationTest {
+ // test body
+}
+
+ Furthermore, it is sometimes necessary to resolve active
+ profiles for tests programmatically instead of
+ declaratively — for example, based on:
+
+
+
+ the current operating system
+
+
+
+ whether tests are being executed on a continuous integration
+ build server
+
+
+
+ the presence of certain environment variables
+
+
+
+ the presence of custom class-level annotations
+
+
+
+ etc.
+
+
+
+ To resolve active bean definition profiles programmatically,
+ simply implement a custom
+ ActiveProfilesResolver and register it
+ via the resolver attribute of
+ @ActiveProfiles. The following example
+ demonstrates how to implement and register a custom
+ OperatingSystemActiveProfilesResolver. For
+ further information, refer to the respective Javadoc.
+
+ package com.bank.service;
+
+// "dev" profile overridden programmatically via a custom resolver
+@ActiveProfiles(
+ resolver = OperatingSystemActiveProfilesResolver.class,
+ inheritProfiles = false)
+public class TransferServiceTest extends AbstractIntegrationTest {
+ // test body
+}
+
+ package com.bank.service.test;
+
+public class OperatingSystemActiveProfilesResolver implements ActiveProfilesResolver {
+
+ @Override
+ String[] resolve(Class<?> testClass) {
+ String profile = ...;
+ // determine the value of profile based on the operating system
+ return new String[] {profile};
+ }
+}
@@ -2416,11 +2527,11 @@ public class ExtendedTests extends BaseTests {}
injection.
@RunWith(SpringJUnit4ClassRunner.class)
-// specifies the Spring configuration to load for this test fixture
+// specifies the Spring configuration to load for this test fixture
@ContextConfiguration("repository-config.xml")
public class HibernateTitleRepositoryTests {
- // this instance will be dependency injected by type
+ // this instance will be dependency injected by type
@Autowired
private HibernateTitleRepository titleRepository;
@@ -2436,11 +2547,11 @@ public class HibernateTitleRepositoryTests {
below.
@RunWith(SpringJUnit4ClassRunner.class)
-// specifies the Spring configuration to load for this test fixture
+// specifies the Spring configuration to load for this test fixture
@ContextConfiguration("repository-config.xml")
public class HibernateTitleRepositoryTests {
- // this instance will be dependency injected by type
+ // this instance will be dependency injected by type
private HibernateTitleRepository titleRepository;
@Autowired
@@ -2490,7 +2601,7 @@ public class HibernateTitleRepositoryTests {
specific target bean as follows, but make sure to delegate to the
overridden method in the superclass as well.
- // ...
+ // ...
@Autowired
@Override
@@ -2498,7 +2609,7 @@ public class HibernateTitleRepositoryTests {
super.setDataSource(dataSource);
}
-// ...
+// ...
The specified qualifier value indicates the specific
DataSource bean to inject, narrowing
@@ -2749,29 +2860,29 @@ public class FictitiousTransactionalTest {
@BeforeTransaction
public void verifyInitialDatabaseState() {
- // logic to verify the initial state before a transaction is started
+ // logic to verify the initial state before a transaction is started
}
@Before
public void setUpTestDataWithinTransaction() {
- // set up test data within the transaction
+ // set up test data within the transaction
}
@Test
- // overrides the class-level defaultRollback setting
+ // overrides the class-level defaultRollback setting
@Rollback(true)
public void modifyDatabaseWithinTransaction() {
- // logic which uses the test data and modifies database state
+ // logic which uses the test data and modifies database state
}
@After
public void tearDownWithinTransaction() {
- // execute "tear down" logic within the transaction
+ // execute "tear down" logic within the transaction
}
@AfterTransaction
public void verifyFinalDatabaseState() {
- // logic to verify the final state after transaction has rolled back
+ // logic to verify the final state after transaction has rolled back
}
}
@@ -2793,7 +2904,7 @@ public class FictitiousTransactionalTest {
frameworks that maintain an in-memory unit of
work.
- // ...
+ // ...
@Autowired
private SessionFactory sessionFactory;
@@ -2812,7 +2923,7 @@ public void updateWithSessionFlush() {
sessionFactory.getCurrentSession().flush();
}
-// ...
+// ...
@@ -2922,7 +3033,7 @@ public class SimpleTest {
@Test
public void testMethod() {
- // execute test logic...
+ // execute test logic...
}
}
@@ -3226,8 +3337,7 @@ public class MyWebTests {
<bean id="accountService" class="org.mockito.Mockito" factory-method="mock">
<constructor-arg value="org.example.AccountService"/>
-</bean>
-
+</bean>
Then you can inject the mock service into the test in order set
up and verify expectations:
@@ -3274,26 +3384,22 @@ public class AccountTests {
additional builder-style methods corresponding to properties of
MockHttpServletRequest. For example:
- mockMvc.perform(post("/hotels/{id}", 42).accept(MediaType.APPLICATION_JSON));
-
+ mockMvc.perform(post("/hotels/{id}", 42).accept(MediaType.APPLICATION_JSON));
In addition to all the HTTP methods, you can also perform file
upload requests, which internally creates an instance of
MockMultipartHttpServletRequest:
- mockMvc.perform(fileUpload("/doc").file("a1", "ABC".getBytes("UTF-8")));
-
+ mockMvc.perform(fileUpload("/doc").file("a1", "ABC".getBytes("UTF-8")));
Query string parameters can be specified in the URI
template:
- mockMvc.perform(get("/hotels?foo={foo}", "bar"));
-
+ mockMvc.perform(get("/hotels?foo={foo}", "bar"));
Or by adding Servlet request parameters:
- mockMvc.perform(get("/hotels").param("foo", "bar"));
-
+ mockMvc.perform(get("/hotels").param("foo", "bar"));
If application code relies on Servlet request parameters, and
doesn't check the query string, as is most often the case, then it
@@ -3308,8 +3414,7 @@ public class AccountTests {
servletPath accordingly so that request mappings
will work:
- mockMvc.perform(get("/app/main/hotels/{id}").contextPath("/app").servletPath("/main"))
-
+ mockMvc.perform(get("/app/main/hotels/{id}").contextPath("/app").servletPath("/main"))
Looking at the above example, it would be cumbersome to set the
contextPath and servletPath with every performed request. That's why
@@ -3326,9 +3431,7 @@ public class AccountTests {
.defaultRequest(get("/")
.contextPath("/app").servletPath("/main")
.accept(MediaType.APPLICATION_JSON).build();
- }
-
-}
+ }
The above properties will apply to every request performed
through the MockMvc. If the same property is
@@ -3345,8 +3448,7 @@ public class AccountTests {
.andExpect(..) after call to perform the
request:
- mockMvc.perform(get("/accounts/1")).andExpect(status().isOk());
-
+ mockMvc.perform(get("/accounts/1")).andExpect(status().isOk());
MockMvcResultMatchers.* defines a number of
static members, some of which return types with additional methods,
@@ -3369,8 +3471,7 @@ public class AccountTests {
mockMvc.perform(post("/persons"))
.andExpect(status().isOk())
- .andExpect(model().attributeHasErrors("person"));
-
+ .andExpect(model().attributeHasErrors("person"));
Many times when writing tests, it's useful to dump the result of
the performed request. This can be done as follows, where
@@ -3518,7 +3619,7 @@ mockServer.verify();
shown below:
import static org.junit.Assert.assertEquals;
-// import ...
+// import ...
@ContextConfiguration
public abstract class AbstractClinicTests extends AbstractTransactionalJUnit4SpringContextTests {
@@ -3535,10 +3636,10 @@ public abstract class AbstractClinicTests extends Abstract
assertEquals("Leary", v1.getLastName());
assertEquals(1, v1.getNrOfSpecialties());
assertEquals("radiology", (v1.getSpecialties().get(0)).getName());
- // ...
+ // ...
}
- // ...
+ // ...
}
Notes:
@@ -3608,8 +3709,7 @@ public abstract class AbstractClinicTests extends Abstract
AbstractClinicTests-context.xml.
@ContextConfiguration
-public class HibernateClinicTests extends AbstractClinicTests { }
-
+public class HibernateClinicTests extends AbstractClinicTests { }
In a large-scale application, the Spring configuration is often
split across multiple files. Consequently, configuration locations are