Browse Source

Introduce BEFORE METHOD/CLASS modes in @DirtiesContext

Prior to this commit, @DirtiesContext could only be used to close a
test ApplicationContext after an entire test class or after a test
method; however, there are some use cases for which it would be
beneficial to close a test ApplicationContext before a given test class
or test method -- for example, if some rogue (i.e., yet to be
determined) test within a large test suite has corrupted the original
configuration for the ApplicationContext.

This commit provides a solution to such testing challenges by
introducing the following modes for @DirtiesContext.

 - MethodMode.BEFORE_METHOD: configured via the new methodMode attribute

 - ClassMode.BEFORE_CLASS and ClassMode.BEFORE_EACH_TEST_METHOD: both
   configured via the existing classMode attribute

Issue: SPR-12429
pull/759/head
Sam Brannen 10 years ago
parent
commit
e086a637d5
  1. 112
      spring-test/src/main/java/org/springframework/test/annotation/DirtiesContext.java
  2. 165
      spring-test/src/main/java/org/springframework/test/context/support/DirtiesContextTestExecutionListener.java
  3. 156
      spring-test/src/test/java/org/springframework/test/context/support/DirtiesContextTestExecutionListenerTests.java
  4. 72
      src/asciidoc/testing.adoc

112
spring-test/src/main/java/org/springframework/test/annotation/DirtiesContext.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2014 the original author or authors.
* Copyright 2002-2015 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.
@ -26,36 +26,49 @@ import java.lang.annotation.Target; @@ -26,36 +26,49 @@ import java.lang.annotation.Target;
/**
* Test annotation which indicates that the
* {@link org.springframework.context.ApplicationContext ApplicationContext}
* associated with a test is <em>dirty</em> and should be closed:
* associated with a test is <em>dirty</em> and should therefore be closed
* and removed from the context cache.
*
* <ul>
* <li>after the current test, when declared at the method level</li>
* <li>after each test method in the current test class, when declared at the
* class level with class mode set to {@link ClassMode#AFTER_EACH_TEST_METHOD
* AFTER_EACH_TEST_METHOD}</li>
* <li>after the current test class, when declared at the class level with class
* mode set to {@link ClassMode#AFTER_CLASS AFTER_CLASS}</li>
* </ul>
*
* <p>Use this annotation if a test has modified the context &mdash; for example,
* by replacing a bean definition or changing the state of a singleton bean.
* Subsequent tests will be supplied a new context.
* <p>Use this annotation if a test has modified the context &mdash; for
* example, by modifying the state of a singleton bean, modifying the state
* of an embedded database, etc. Subsequent tests that request the same
* context will be supplied a new context.
*
* <p>{@code @DirtiesContext} may be used as a class-level and method-level
* annotation within the same class. In such scenarios, the
* {@code ApplicationContext} will be marked as <em>dirty</em> after any
* such annotated method as well as after the entire class. If the
* {@link ClassMode} is set to {@link ClassMode#AFTER_EACH_TEST_METHOD
* AFTER_EACH_TEST_METHOD}, the context will be marked dirty after each test
* method in the class.
* annotation within the same class or class hierarchy. In such scenarios, the
* {@code ApplicationContext} will be marked as <em>dirty</em> before or
* after any such annotated method as well as before or after the current test
* class, depending on the configured {@link #methodMode} and {@link #classMode}.
*
* <p>As of Spring Framework 4.0, this annotation may be used as a
* <em>meta-annotation</em> to create custom <em>composed annotations</em>.
*
* <h3>Supported Test Phases</h3>
* <ul>
* <li><strong>Before current test class</strong>: when declared at the class
* level with class mode set to {@link ClassMode#BEFORE_CLASS BEFORE_CLASS}</li>
* <li><strong>Before each test method in current test class</strong>: when
* declared at the class level with class mode set to
* {@link ClassMode#BEFORE_EACH_TEST_METHOD BEFORE_EACH_TEST_METHOD}</li>
* <li><strong>Before current test method</strong>: when declared at the
* method level with method mode set to
* {@link MethodMode#BEFORE_METHOD BEFORE_METHOD}</li>
* <li><strong>After current test method</strong>: when declared at the
* method level with method mode set to
* {@link MethodMode#AFTER_METHOD AFTER_METHOD}</li>
* <li><strong>After each test method in current test class</strong>: when
* declared at the class level with class mode set to
* {@link ClassMode#AFTER_EACH_TEST_METHOD AFTER_EACH_TEST_METHOD}</li>
* <li><strong>After current test class</strong>: when declared at the
* class level with class mode set to
* {@link ClassMode#AFTER_CLASS AFTER_CLASS}</li>
* </ul>
*
* @author Sam Brannen
* @author Rod Johnson
* @since 2.0
* @see org.springframework.test.context.ContextConfiguration
* @see org.springframework.test.context.support.DirtiesContextTestExecutionListener
*/
@Documented
@Inherited
@ -63,6 +76,27 @@ import java.lang.annotation.Target; @@ -63,6 +76,27 @@ import java.lang.annotation.Target;
@Target({ ElementType.TYPE, ElementType.METHOD })
public @interface DirtiesContext {
/**
* Defines <i>modes</i> which determine how {@code @DirtiesContext} is
* interpreted when used to annotate a test method.
*
* @since 4.2
*/
static enum MethodMode {
/**
* The associated {@code ApplicationContext} will be marked as
* <em>dirty</em> before the corresponding test method.
*/
BEFORE_METHOD,
/**
* The associated {@code ApplicationContext} will be marked as
* <em>dirty</em> after the corresponding test method.
*/
AFTER_METHOD;
}
/**
* Defines <i>modes</i> which determine how {@code @DirtiesContext} is
* interpreted when used to annotate a test class.
@ -73,15 +107,31 @@ public @interface DirtiesContext { @@ -73,15 +107,31 @@ public @interface DirtiesContext {
/**
* The associated {@code ApplicationContext} will be marked as
* <em>dirty</em> after the test class.
* <em>dirty</em> before the test class.
*
* @since 4.2
*/
AFTER_CLASS,
BEFORE_CLASS,
/**
* The associated {@code ApplicationContext} will be marked as
* <em>dirty</em> before each test method in the class.
*
* @since 4.2
*/
BEFORE_EACH_TEST_METHOD,
/**
* The associated {@code ApplicationContext} will be marked as
* <em>dirty</em> after each test method in the class.
*/
AFTER_EACH_TEST_METHOD;
AFTER_EACH_TEST_METHOD,
/**
* The associated {@code ApplicationContext} will be marked as
* <em>dirty</em> after the test class.
*/
AFTER_CLASS;
}
/**
@ -119,13 +169,23 @@ public @interface DirtiesContext { @@ -119,13 +169,23 @@ public @interface DirtiesContext {
}
/**
* The <i>mode</i> to use when a test method is annotated with
* {@code @DirtiesContext}.
* <p>Defaults to {@link MethodMode#AFTER_METHOD AFTER_METHOD}.
* <p>Setting the method mode on an annotated test class has no meaning.
* For class-level control, use {@link #classMode} instead.
*
* @since 4.2
*/
MethodMode methodMode() default MethodMode.AFTER_METHOD;
/**
* The <i>mode</i> to use when a test class is annotated with
* {@code @DirtiesContext}.
* <p>Defaults to {@link ClassMode#AFTER_CLASS AFTER_CLASS}.
* <p>Note: Setting the class mode on an annotated test method has no meaning,
* since the mere presence of the {@code @DirtiesContext} annotation on a
* test method is sufficient.
* <p>Setting the class mode on an annotated test method has no meaning.
* For method-level control, use {@link #methodMode} instead.
*
* @since 3.0
*/

165
spring-test/src/main/java/org/springframework/test/context/support/DirtiesContextTestExecutionListener.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2014 the original author or authors.
* Copyright 2002-2015 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.
@ -27,16 +27,18 @@ import org.springframework.core.annotation.AnnotationAttributes; @@ -27,16 +27,18 @@ import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.annotation.DirtiesContext.ClassMode;
import org.springframework.test.annotation.DirtiesContext.HierarchyMode;
import org.springframework.test.annotation.DirtiesContext.MethodMode;
import org.springframework.test.context.TestContext;
import org.springframework.util.Assert;
import static org.springframework.test.annotation.DirtiesContext.ClassMode.*;
import static org.springframework.test.annotation.DirtiesContext.MethodMode.*;
/**
* {@code TestExecutionListener} which provides support for marking the
* {@code ApplicationContext} associated with a test as <em>dirty</em> for
* both test classes and test methods configured with the {@link DirtiesContext
* &#064;DirtiesContext} annotation.
* both test classes and test methods annotated with the
* {@link DirtiesContext @DirtiesContext} annotation.
*
* @author Sam Brannen
* @author Juergen Hoeller
@ -56,20 +58,98 @@ public class DirtiesContextTestExecutionListener extends AbstractTestExecutionLi @@ -56,20 +58,98 @@ public class DirtiesContextTestExecutionListener extends AbstractTestExecutionLi
return 3000;
}
/**
* If the test class of the supplied {@linkplain TestContext test context}
* is annotated with {@code @DirtiesContext} and the {@linkplain
* DirtiesContext#classMode() class mode} is set to {@link
* ClassMode#BEFORE_CLASS BEFORE_CLASS}, the {@linkplain ApplicationContext
* application context} of the test context will be
* {@linkplain TestContext#markApplicationContextDirty marked as dirty}, and the
* {@link DependencyInjectionTestExecutionListener#REINJECT_DEPENDENCIES_ATTRIBUTE
* REINJECT_DEPENDENCIES_ATTRIBUTE} in the test context will be set to
* {@code true}.
*/
@Override
public void beforeTestClass(TestContext testContext) throws Exception {
beforeOrAfterTestClass(testContext, "Before", BEFORE_CLASS);
}
/**
* If the current test method of the supplied {@linkplain TestContext test
* context} is annotated with {@link DirtiesContext &#064;DirtiesContext},
* or if the test class is annotated with {@link DirtiesContext
* &#064;DirtiesContext} and the {@linkplain DirtiesContext#classMode() class
* mode} is set to {@link ClassMode#AFTER_EACH_TEST_METHOD
* AFTER_EACH_TEST_METHOD}, the {@linkplain ApplicationContext application
* context} of the test context will be
* {@linkplain TestContext#markApplicationContextDirty marked as dirty} and the
* {@link DependencyInjectionTestExecutionListener#REINJECT_DEPENDENCIES_ATTRIBUTE}
* in the test context will be set to {@code true}.
* context} is annotated with {@code @DirtiesContext} and the {@linkplain
* DirtiesContext#methodMode() method mode} is set to {@link
* MethodMode#BEFORE_METHOD BEFORE_METHOD}, or if the test class is
* annotated with {@code @DirtiesContext} and the {@linkplain
* DirtiesContext#classMode() class mode} is set to {@link
* ClassMode#BEFORE_EACH_TEST_METHOD BEFORE_EACH_TEST_METHOD}, the
* {@linkplain ApplicationContext application context} of the test context
* will be {@linkplain TestContext#markApplicationContextDirty marked as dirty} and the
* {@link DependencyInjectionTestExecutionListener#REINJECT_DEPENDENCIES_ATTRIBUTE
* REINJECT_DEPENDENCIES_ATTRIBUTE} in the test context will be set to {@code true}.
* @since 4.2
*/
@Override
public void beforeTestMethod(TestContext testContext) throws Exception {
beforeOrAfterTestMethod(testContext, "Before", BEFORE_METHOD, BEFORE_EACH_TEST_METHOD);
}
/**
* If the current test method of the supplied {@linkplain TestContext test
* context} is annotated with {@code @DirtiesContext} and the {@linkplain
* DirtiesContext#methodMode() method mode} is set to {@link
* MethodMode#AFTER_METHOD AFTER_METHOD}, or if the test class is
* annotated with {@code @DirtiesContext} and the {@linkplain
* DirtiesContext#classMode() class mode} is set to {@link
* ClassMode#AFTER_EACH_TEST_METHOD AFTER_EACH_TEST_METHOD}, the
* {@linkplain ApplicationContext application context} of the test context
* will be {@linkplain TestContext#markApplicationContextDirty marked as dirty} and the
* {@link DependencyInjectionTestExecutionListener#REINJECT_DEPENDENCIES_ATTRIBUTE
* REINJECT_DEPENDENCIES_ATTRIBUTE} in the test context will be set to {@code true}.
*/
@Override
public void afterTestMethod(TestContext testContext) throws Exception {
beforeOrAfterTestMethod(testContext, "After", AFTER_METHOD, AFTER_EACH_TEST_METHOD);
}
/**
* If the test class of the supplied {@linkplain TestContext test context}
* is annotated with {@code @DirtiesContext} and the {@linkplain
* DirtiesContext#classMode() class mode} is set to {@link
* ClassMode#AFTER_CLASS AFTER_CLASS}, the {@linkplain ApplicationContext
* application context} of the test context will be
* {@linkplain TestContext#markApplicationContextDirty marked as dirty}, and the
* {@link DependencyInjectionTestExecutionListener#REINJECT_DEPENDENCIES_ATTRIBUTE
* REINJECT_DEPENDENCIES_ATTRIBUTE} in the test context will be set to
* {@code true}.
*/
@Override
public void afterTestClass(TestContext testContext) throws Exception {
beforeOrAfterTestClass(testContext, "After", AFTER_CLASS);
}
/**
* Marks the {@linkplain ApplicationContext application context} of the supplied
* {@linkplain TestContext test context} as
* {@linkplain TestContext#markApplicationContextDirty(DirtiesContext.HierarchyMode) dirty}
* and sets {@link DependencyInjectionTestExecutionListener#REINJECT_DEPENDENCIES_ATTRIBUTE
* REINJECT_DEPENDENCIES_ATTRIBUTE} in the test context to {@code true}.
* @param testContext the test context whose application context should
* marked as dirty
* @param hierarchyMode the context cache clearing mode to be applied if the
* context is part of a hierarchy; may be {@code null}
* @since 3.2.2
*/
protected void dirtyContext(TestContext testContext, HierarchyMode hierarchyMode) {
testContext.markApplicationContextDirty(hierarchyMode);
testContext.setAttribute(DependencyInjectionTestExecutionListener.REINJECT_DEPENDENCIES_ATTRIBUTE, Boolean.TRUE);
}
/**
* Perform the actual work for {@link #beforeTestMethod} and {@link #afterTestMethod}.
* @since 4.2
*/
private void beforeOrAfterTestMethod(TestContext testContext, String phase, MethodMode requiredMethodMode,
ClassMode requiredClassMode) throws Exception {
Class<?> testClass = testContext.getTestClass();
Assert.notNull(testClass, "The test class of the supplied TestContext must not be null");
Method testMethod = testContext.getTestMethod();
@ -78,67 +158,48 @@ public class DirtiesContextTestExecutionListener extends AbstractTestExecutionLi @@ -78,67 +158,48 @@ public class DirtiesContextTestExecutionListener extends AbstractTestExecutionLi
final String annotationType = DirtiesContext.class.getName();
AnnotationAttributes methodAnnAttrs = AnnotatedElementUtils.getAnnotationAttributes(testMethod, annotationType);
AnnotationAttributes classAnnAttrs = AnnotatedElementUtils.getAnnotationAttributes(testClass, annotationType);
boolean methodDirtiesContext = methodAnnAttrs != null;
boolean classDirtiesContext = classAnnAttrs != null;
ClassMode classMode = classDirtiesContext ? classAnnAttrs.<ClassMode> getEnum("classMode") : null;
boolean methodAnnotated = methodAnnAttrs != null;
boolean classAnnotated = classAnnAttrs != null;
MethodMode methodMode = methodAnnotated ? methodAnnAttrs.<MethodMode> getEnum("methodMode") : null;
ClassMode classMode = classAnnotated ? classAnnAttrs.<ClassMode> getEnum("classMode") : null;
if (logger.isDebugEnabled()) {
logger.debug(String.format(
"After test method: context %s, class dirties context [%s], class mode [%s], method dirties context [%s].",
testContext, classDirtiesContext, classMode, methodDirtiesContext));
"%s test method: context %s, class annotated with @DirtiesContext [%s] with mode [%s], method annotated with @DirtiesContext [%s] with mode [%s].",
phase, testContext, classAnnotated, classMode, methodAnnotated, methodMode));
}
if (methodDirtiesContext || (classMode == AFTER_EACH_TEST_METHOD)) {
HierarchyMode hierarchyMode = methodDirtiesContext ? methodAnnAttrs.<HierarchyMode> getEnum("hierarchyMode")
if ((methodMode == requiredMethodMode) || (classMode == requiredClassMode)) {
HierarchyMode hierarchyMode = methodAnnotated ? methodAnnAttrs.<HierarchyMode> getEnum("hierarchyMode")
: classAnnAttrs.<HierarchyMode> getEnum("hierarchyMode");
dirtyContext(testContext, hierarchyMode);
}
}
/**
* If the test class of the supplied {@linkplain TestContext test context} is
* annotated with {@link DirtiesContext &#064;DirtiesContext}, the
* {@linkplain ApplicationContext application context} of the test context will
* be {@linkplain TestContext#markApplicationContextDirty marked as dirty},
* and the
* {@link DependencyInjectionTestExecutionListener#REINJECT_DEPENDENCIES_ATTRIBUTE
* REINJECT_DEPENDENCIES_ATTRIBUTE} in the test context will be set to
* {@code true}.
* Perform the actual work for {@link #beforeTestClass} and {@link #afterTestClass}.
* @since 4.2
*/
@Override
public void afterTestClass(TestContext testContext) throws Exception {
private void beforeOrAfterTestClass(TestContext testContext, String phase, ClassMode requiredClassMode)
throws Exception {
Class<?> testClass = testContext.getTestClass();
Assert.notNull(testClass, "The test class of the supplied TestContext must not be null");
final String annotationType = DirtiesContext.class.getName();
AnnotationAttributes annAttrs = AnnotatedElementUtils.getAnnotationAttributes(testClass, annotationType);
boolean dirtiesContext = annAttrs != null;
AnnotationAttributes classAnnAttrs = AnnotatedElementUtils.getAnnotationAttributes(testClass, annotationType);
boolean classAnnotated = classAnnAttrs != null;
ClassMode classMode = classAnnotated ? classAnnAttrs.<ClassMode> getEnum("classMode") : null;
if (logger.isDebugEnabled()) {
logger.debug(String.format("After test class: context %s, dirtiesContext [%s].", testContext,
dirtiesContext));
logger.debug(String.format(
"%s test class: context %s, class annotated with @DirtiesContext [%s] with mode [%s].", phase,
testContext, classAnnotated, classMode));
}
if (dirtiesContext) {
HierarchyMode hierarchyMode = annAttrs.<HierarchyMode> getEnum("hierarchyMode");
if (classMode == requiredClassMode) {
HierarchyMode hierarchyMode = classAnnAttrs.<HierarchyMode> getEnum("hierarchyMode");
dirtyContext(testContext, hierarchyMode);
}
}
/**
* Marks the {@linkplain ApplicationContext application context} of the supplied
* {@linkplain TestContext test context} as
* {@linkplain TestContext#markApplicationContextDirty(DirtiesContext.HierarchyMode) dirty}
* and sets {@link DependencyInjectionTestExecutionListener#REINJECT_DEPENDENCIES_ATTRIBUTE}
* in the test context to {@code true}.
* @param testContext the test context whose application context should
* marked as dirty
* @param hierarchyMode the context cache clearing mode to be applied if the
* context is part of a hierarchy; may be {@code null}
* @since 3.2.2
*/
protected void dirtyContext(TestContext testContext, HierarchyMode hierarchyMode) {
testContext.markApplicationContextDirty(hierarchyMode);
testContext.setAttribute(DependencyInjectionTestExecutionListener.REINJECT_DEPENDENCIES_ATTRIBUTE, Boolean.TRUE);
}
}

156
spring-test/src/test/java/org/springframework/test/context/support/DirtiesContextTestExecutionListenerTests.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2015 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.
@ -20,6 +20,7 @@ import java.lang.annotation.Retention; @@ -20,6 +20,7 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import org.junit.Test;
import org.mockito.BDDMockito;
import org.springframework.test.annotation.DirtiesContext;
@ -28,8 +29,10 @@ import org.springframework.test.annotation.DirtiesContext.HierarchyMode; @@ -28,8 +29,10 @@ import org.springframework.test.annotation.DirtiesContext.HierarchyMode;
import org.springframework.test.context.TestContext;
import static org.mockito.BDDMockito.*;
import static org.springframework.test.annotation.DirtiesContext.ClassMode.*;
import static org.springframework.test.annotation.DirtiesContext.HierarchyMode.*;
import static org.springframework.test.annotation.DirtiesContext.MethodMode.*;
/**
* Unit tests for {@link DirtiesContextTestExecutionListener}.
@ -44,64 +47,113 @@ public class DirtiesContextTestExecutionListenerTests { @@ -44,64 +47,113 @@ public class DirtiesContextTestExecutionListenerTests {
@Test
public void afterTestMethodForDirtiesContextDeclaredLocallyOnMethod() throws Exception {
public void beforeAndAfterTestMethodForDirtiesContextDeclaredLocallyOnMethodWithBeforeMethodMode() throws Exception {
Class<?> clazz = getClass();
BDDMockito.<Class<?>> given(testContext.getTestClass()).willReturn(clazz);
given(testContext.getTestMethod()).willReturn(clazz.getDeclaredMethod("dirtiesContextDeclaredLocally"));
given(testContext.getTestMethod()).willReturn(
clazz.getDeclaredMethod("dirtiesContextDeclaredLocallyWithBeforeMethodMode"));
listener.beforeTestMethod(testContext);
verify(testContext, times(1)).markApplicationContextDirty(EXHAUSTIVE);
listener.afterTestMethod(testContext);
verify(testContext, times(1)).markApplicationContextDirty(EXHAUSTIVE);
}
@Test
public void afterTestMethodForDirtiesContextDeclaredOnMethodViaMetaAnnotation() throws Exception {
public void beforeAndAfterTestMethodForDirtiesContextDeclaredLocallyOnMethodWithAfterMethodMode() throws Exception {
Class<?> clazz = getClass();
BDDMockito.<Class<?>> given(testContext.getTestClass()).willReturn(clazz);
given(testContext.getTestMethod()).willReturn(clazz.getDeclaredMethod("dirtiesContextDeclaredViaMetaAnnotation"));
given(testContext.getTestMethod()).willReturn(
clazz.getDeclaredMethod("dirtiesContextDeclaredLocallyWithAfterMethodMode"));
listener.beforeTestMethod(testContext);
verify(testContext, times(0)).markApplicationContextDirty(any(HierarchyMode.class));
listener.afterTestMethod(testContext);
verify(testContext, times(1)).markApplicationContextDirty(EXHAUSTIVE);
}
@Test
public void beforeAndAfterTestMethodForDirtiesContextDeclaredOnMethodViaMetaAnnotationWithAfterMethodMode()
throws Exception {
Class<?> clazz = getClass();
BDDMockito.<Class<?>> given(testContext.getTestClass()).willReturn(clazz);
given(testContext.getTestMethod()).willReturn(
clazz.getDeclaredMethod("dirtiesContextDeclaredViaMetaAnnotationWithAfterMethodMode"));
listener.beforeTestMethod(testContext);
verify(testContext, times(0)).markApplicationContextDirty(any(HierarchyMode.class));
listener.afterTestMethod(testContext);
verify(testContext, times(1)).markApplicationContextDirty(EXHAUSTIVE);
}
@Test
public void beforeAndAfterTestMethodForDirtiesContextDeclaredLocallyOnClassBeforeEachTestMethod() throws Exception {
Class<?> clazz = DirtiesContextDeclaredLocallyBeforeEachTestMethod.class;
BDDMockito.<Class<?>> given(testContext.getTestClass()).willReturn(clazz);
given(testContext.getTestMethod()).willReturn(clazz.getDeclaredMethod("clean"));
listener.beforeTestMethod(testContext);
verify(testContext, times(1)).markApplicationContextDirty(EXHAUSTIVE);
listener.afterTestMethod(testContext);
verify(testContext, times(1)).markApplicationContextDirty(EXHAUSTIVE);
}
@Test
public void afterTestMethodForDirtiesContextDeclaredLocallyOnClassAfterEachTestMethod() throws Exception {
public void beforeAndAfterTestMethodForDirtiesContextDeclaredLocallyOnClassAfterEachTestMethod() throws Exception {
Class<?> clazz = DirtiesContextDeclaredLocallyAfterEachTestMethod.class;
BDDMockito.<Class<?>> given(testContext.getTestClass()).willReturn(clazz);
given(testContext.getTestMethod()).willReturn(clazz.getDeclaredMethod("clean"));
listener.beforeTestMethod(testContext);
verify(testContext, times(0)).markApplicationContextDirty(any(HierarchyMode.class));
listener.afterTestMethod(testContext);
verify(testContext, times(1)).markApplicationContextDirty(EXHAUSTIVE);
}
@Test
public void afterTestMethodForDirtiesContextDeclaredViaMetaAnnotationOnClassAfterEachTestMethod() throws Exception {
public void beforeAndAfterTestMethodForDirtiesContextDeclaredViaMetaAnnotationOnClassAfterEachTestMethod()
throws Exception {
Class<?> clazz = DirtiesContextDeclaredViaMetaAnnotationAfterEachTestMethod.class;
BDDMockito.<Class<?>> given(testContext.getTestClass()).willReturn(clazz);
given(testContext.getTestMethod()).willReturn(clazz.getDeclaredMethod("clean"));
listener.beforeTestMethod(testContext);
verify(testContext, times(0)).markApplicationContextDirty(any(HierarchyMode.class));
listener.afterTestMethod(testContext);
verify(testContext, times(1)).markApplicationContextDirty(EXHAUSTIVE);
}
@Test
public void afterTestMethodForDirtiesContextDeclaredLocallyOnClassAfterClass() throws Exception {
public void beforeAndAfterTestMethodForDirtiesContextDeclaredLocallyOnClassBeforeClass() throws Exception {
Class<?> clazz = DirtiesContextDeclaredLocallyBeforeClass.class;
BDDMockito.<Class<?>> given(testContext.getTestClass()).willReturn(clazz);
given(testContext.getTestMethod()).willReturn(clazz.getDeclaredMethod("clean"));
listener.beforeTestMethod(testContext);
listener.afterTestMethod(testContext);
verify(testContext, times(0)).markApplicationContextDirty(any(HierarchyMode.class));
}
@Test
public void beforeAndAfterTestMethodForDirtiesContextDeclaredLocallyOnClassAfterClass() throws Exception {
Class<?> clazz = DirtiesContextDeclaredLocallyAfterClass.class;
BDDMockito.<Class<?>> given(testContext.getTestClass()).willReturn(clazz);
given(testContext.getTestMethod()).willReturn(clazz.getDeclaredMethod("clean"));
listener.beforeTestMethod(testContext);
listener.afterTestMethod(testContext);
verify(testContext, times(0)).markApplicationContextDirty(EXHAUSTIVE);
verify(testContext, times(0)).markApplicationContextDirty(any(HierarchyMode.class));
}
@Test
public void afterTestMethodForDirtiesContextDeclaredViaMetaAnnotationOnClassAfterClass() throws Exception {
public void beforeAndAfterTestMethodForDirtiesContextDeclaredViaMetaAnnotationOnClassAfterClass() throws Exception {
Class<?> clazz = DirtiesContextDeclaredViaMetaAnnotationAfterClass.class;
BDDMockito.<Class<?>> given(testContext.getTestClass()).willReturn(clazz);
given(testContext.getTestMethod()).willReturn(clazz.getDeclaredMethod("clean"));
listener.beforeTestMethod(testContext);
listener.afterTestMethod(testContext);
verify(testContext, times(0)).markApplicationContextDirty(EXHAUSTIVE);
verify(testContext, times(0)).markApplicationContextDirty(any(HierarchyMode.class));
}
@Test
public void afterTestMethodForDirtiesContextViaMetaAnnotationWithOverrides() throws Exception {
public void beforeAndAfterTestMethodForDirtiesContextViaMetaAnnotationWithOverrides() throws Exception {
Class<?> clazz = DirtiesContextViaMetaAnnotationWithOverrides.class;
BDDMockito.<Class<?>> given(testContext.getTestClass()).willReturn(clazz);
given(testContext.getTestMethod()).willReturn(clazz.getDeclaredMethod("clean"));
listener.beforeTestMethod(testContext);
verify(testContext, times(0)).markApplicationContextDirty(any(HierarchyMode.class));
listener.afterTestMethod(testContext);
verify(testContext, times(1)).markApplicationContextDirty(CURRENT_LEVEL);
}
@ -109,77 +161,113 @@ public class DirtiesContextTestExecutionListenerTests { @@ -109,77 +161,113 @@ public class DirtiesContextTestExecutionListenerTests {
// -------------------------------------------------------------------------
@Test
public void afterTestClassForDirtiesContextDeclaredLocallyOnMethod() throws Exception {
public void beforeAndAfterTestClassForDirtiesContextDeclaredLocallyOnMethod() throws Exception {
Class<?> clazz = getClass();
BDDMockito.<Class<?>> given(testContext.getTestClass()).willReturn(clazz);
listener.beforeTestClass(testContext);
listener.afterTestClass(testContext);
verify(testContext, times(0)).markApplicationContextDirty(any(HierarchyMode.class));
}
@Test
public void beforeAndAfterTestClassForDirtiesContextDeclaredLocallyOnClassBeforeEachTestMethod() throws Exception {
Class<?> clazz = DirtiesContextDeclaredLocallyBeforeEachTestMethod.class;
BDDMockito.<Class<?>> given(testContext.getTestClass()).willReturn(clazz);
listener.beforeTestClass(testContext);
listener.afterTestClass(testContext);
verify(testContext, times(0)).markApplicationContextDirty(EXHAUSTIVE);
verify(testContext, times(0)).markApplicationContextDirty(any(HierarchyMode.class));
}
@Test
public void afterTestClassForDirtiesContextDeclaredLocallyOnClassAfterEachTestMethod() throws Exception {
public void beforeAndAfterTestClassForDirtiesContextDeclaredLocallyOnClassAfterEachTestMethod() throws Exception {
Class<?> clazz = DirtiesContextDeclaredLocallyAfterEachTestMethod.class;
BDDMockito.<Class<?>> given(testContext.getTestClass()).willReturn(clazz);
listener.beforeTestClass(testContext);
listener.afterTestClass(testContext);
verify(testContext, times(1)).markApplicationContextDirty(EXHAUSTIVE);
verify(testContext, times(0)).markApplicationContextDirty(any(HierarchyMode.class));
}
@Test
public void afterTestClassForDirtiesContextDeclaredViaMetaAnnotationOnClassAfterEachTestMethod() throws Exception {
public void beforeAndAfterTestClassForDirtiesContextDeclaredViaMetaAnnotationOnClassAfterEachTestMethod()
throws Exception {
Class<?> clazz = DirtiesContextDeclaredViaMetaAnnotationAfterEachTestMethod.class;
BDDMockito.<Class<?>> given(testContext.getTestClass()).willReturn(clazz);
listener.beforeTestClass(testContext);
listener.afterTestClass(testContext);
verify(testContext, times(0)).markApplicationContextDirty(any(HierarchyMode.class));
}
@Test
public void beforeAndAfterTestClassForDirtiesContextDeclaredLocallyOnClassBeforeClass() throws Exception {
Class<?> clazz = DirtiesContextDeclaredLocallyBeforeClass.class;
BDDMockito.<Class<?>> given(testContext.getTestClass()).willReturn(clazz);
listener.beforeTestClass(testContext);
verify(testContext, times(1)).markApplicationContextDirty(EXHAUSTIVE);
listener.afterTestClass(testContext);
verify(testContext, times(1)).markApplicationContextDirty(EXHAUSTIVE);
}
@Test
public void afterTestClassForDirtiesContextDeclaredLocallyOnClassAfterClass() throws Exception {
public void beforeAndAfterTestClassForDirtiesContextDeclaredLocallyOnClassAfterClass() throws Exception {
Class<?> clazz = DirtiesContextDeclaredLocallyAfterClass.class;
BDDMockito.<Class<?>> given(testContext.getTestClass()).willReturn(clazz);
listener.beforeTestClass(testContext);
verify(testContext, times(0)).markApplicationContextDirty(any(HierarchyMode.class));
listener.afterTestClass(testContext);
verify(testContext, times(1)).markApplicationContextDirty(EXHAUSTIVE);
}
@Test
public void afterTestClassForDirtiesContextDeclaredViaMetaAnnotationOnClassAfterClass() throws Exception {
public void beforeAndAfterTestClassForDirtiesContextDeclaredViaMetaAnnotationOnClassAfterClass() throws Exception {
Class<?> clazz = DirtiesContextDeclaredViaMetaAnnotationAfterClass.class;
BDDMockito.<Class<?>> given(testContext.getTestClass()).willReturn(clazz);
listener.beforeTestClass(testContext);
verify(testContext, times(0)).markApplicationContextDirty(any(HierarchyMode.class));
listener.afterTestClass(testContext);
verify(testContext, times(1)).markApplicationContextDirty(EXHAUSTIVE);
}
@Test
public void afterTestClassForDirtiesContextDeclaredViaMetaAnnotationWithOverrides() throws Exception {
public void beforeAndAfterTestClassForDirtiesContextDeclaredViaMetaAnnotationWithOverrides() throws Exception {
Class<?> clazz = DirtiesContextViaMetaAnnotationWithOverrides.class;
BDDMockito.<Class<?>> given(testContext.getTestClass()).willReturn(clazz);
listener.beforeTestClass(testContext);
listener.afterTestClass(testContext);
verify(testContext, times(1)).markApplicationContextDirty(CURRENT_LEVEL);
verify(testContext, times(0)).markApplicationContextDirty(any(HierarchyMode.class));
}
@Test
public void afterTestClassForDirtiesContextDeclaredViaMetaAnnotationWithOverridenAttributes() throws Exception {
public void beforeAndAfterTestClassForDirtiesContextDeclaredViaMetaAnnotationWithOverridenAttributes()
throws Exception {
Class<?> clazz = DirtiesContextViaMetaAnnotationWithOverridenAttributes.class;
BDDMockito.<Class<?>> given(testContext.getTestClass()).willReturn(clazz);
listener.beforeTestClass(testContext);
verify(testContext, times(0)).markApplicationContextDirty(any(HierarchyMode.class));
listener.afterTestClass(testContext);
verify(testContext, times(1)).markApplicationContextDirty(EXHAUSTIVE);
}
// -------------------------------------------------------------------------
@DirtiesContext(methodMode = BEFORE_METHOD)
void dirtiesContextDeclaredLocallyWithBeforeMethodMode() {
/* no-op */
}
@DirtiesContext
void dirtiesContextDeclaredLocally() {
void dirtiesContextDeclaredLocallyWithAfterMethodMode() {
/* no-op */
}
@MetaDirty
void dirtiesContextDeclaredViaMetaAnnotation() {
@MetaDirtyAfterMethod
void dirtiesContextDeclaredViaMetaAnnotationWithAfterMethodMode() {
/* no-op */
}
@DirtiesContext
@Retention(RetentionPolicy.RUNTIME)
static @interface MetaDirty {
static @interface MetaDirtyAfterMethod {
}
@DirtiesContext(classMode = AFTER_EACH_TEST_METHOD)
@ -192,6 +280,14 @@ public class DirtiesContextTestExecutionListenerTests { @@ -192,6 +280,14 @@ public class DirtiesContextTestExecutionListenerTests {
static @interface MetaDirtyAfterClass {
}
@DirtiesContext(classMode = BEFORE_EACH_TEST_METHOD)
static class DirtiesContextDeclaredLocallyBeforeEachTestMethod {
void clean() {
/* no-op */
}
}
@DirtiesContext(classMode = AFTER_EACH_TEST_METHOD)
static class DirtiesContextDeclaredLocallyAfterEachTestMethod {
@ -217,6 +313,14 @@ public class DirtiesContextTestExecutionListenerTests { @@ -217,6 +313,14 @@ public class DirtiesContextTestExecutionListenerTests {
}
}
@DirtiesContext(classMode = BEFORE_CLASS)
static class DirtiesContextDeclaredLocallyBeforeClass {
void clean() {
/* no-op */
}
}
@DirtiesContext(classMode = AFTER_CLASS)
static class DirtiesContextDeclaredLocallyAfterClass {

72
src/asciidoc/testing.adoc

@ -598,19 +598,17 @@ The following example demonstrates how to declare _inlined_ properties. @@ -598,19 +598,17 @@ The following example demonstrates how to declare _inlined_ properties.
Indicates that the underlying Spring `ApplicationContext` has been __dirtied__ during
the execution of a test (i.e., modified or corrupted in some manner -- for example, by
changing the state of a singleton bean) and should be closed, regardless of whether the
test passed. When an application context is marked __dirty__, it is removed from the
testing framework's cache and closed. As a consequence, the underlying Spring container
will be rebuilt for any subsequent test that requires a context with the same
configuration metadata.
changing the state of a singleton bean) and should be closed. When an application
context is marked __dirty__, it is removed from the testing framework's cache and
closed. As a consequence, the underlying Spring container will be rebuilt for any
subsequent test that requires a context with the same configuration metadata.
+
`@DirtiesContext` can be used as both a class-level and method-level annotation within
the same test class. In such scenarios, the `ApplicationContext` is marked as __dirty__
after any such annotated method as well as after the entire class. If the `ClassMode` is
set to `AFTER_EACH_TEST_METHOD`, the context is marked dirty after each test method in
the class.
the same class or class hierarchy. In such scenarios, the `ApplicationContext` is marked
as __dirty__ before or after any such annotated method as well as before or after the
current test class, depending on the configured `methodMode` and `classMode`.
+
@ -619,6 +617,22 @@ configuration scenarios: @@ -619,6 +617,22 @@ configuration scenarios:
+
** Before the current test class, when declared on a class with class mode set to
`BEFORE_CLASS`.
+
[source,java,indent=0]
[subs="verbatim,quotes"]
----
**@DirtiesContext(classMode = BEFORE_CLASS)**
public class FreshContextTests {
// some tests that require a new Spring container
}
----
+
** After the current test class, when declared on a class with class mode set to
`AFTER_CLASS` (i.e., the default class mode).
@ -635,6 +649,22 @@ configuration scenarios: @@ -635,6 +649,22 @@ configuration scenarios:
+
** Before each test method in the current test class, when declared on a class with class
mode set to `BEFORE_EACH_TEST_METHOD.`
+
[source,java,indent=0]
[subs="verbatim,quotes"]
----
**@DirtiesContext(classMode = BEFORE_EACH_TEST_METHOD)**
public class FreshContextTests {
// some tests that require a new Spring container
}
----
+
** After each test method in the current test class, when declared on a class with class
mode set to `AFTER_EACH_TEST_METHOD.`
@ -643,7 +673,7 @@ mode set to `AFTER_EACH_TEST_METHOD.` @@ -643,7 +673,7 @@ mode set to `AFTER_EACH_TEST_METHOD.`
[source,java,indent=0]
[subs="verbatim,quotes"]
----
**@DirtiesContext**(**classMode** = ClassMode.AFTER_EACH_TEST_METHOD)
**@DirtiesContext(classMode = AFTER_EACH_TEST_METHOD)**
public class ContextDirtyingTests {
// some tests that result in the Spring container being dirtied
}
@ -651,7 +681,25 @@ mode set to `AFTER_EACH_TEST_METHOD.` @@ -651,7 +681,25 @@ mode set to `AFTER_EACH_TEST_METHOD.`
+
** After the current test, when declared on a method.
** Before the current test, when declared on a method with the method mode set to
`BEFORE_METHOD`.
+
[source,java,indent=0]
[subs="verbatim,quotes"]
----
**@DirtiesContext(methodMode = BEFORE_METHOD)**
@Test
public void testProcessWhichRequiresFreshAppCtx() {
// some logic that requires a new Spring container
}
----
+
** After the current test, when declared on a method with the method mode set to
`AFTER_METHOD` (i.e., the default method mode).
+
@ -693,7 +741,7 @@ specified instead, as seen below. @@ -693,7 +741,7 @@ specified instead, as seen below.
public class ExtendedTests extends BaseTests {
@Test
@DirtiesContext(**hierarchyMode = HierarchyMode.CURRENT_LEVEL**)
@DirtiesContext(**hierarchyMode = CURRENT_LEVEL**)
public void test() {
// some logic that results in the child context being dirtied
}

Loading…
Cancel
Save