@DirtiesContext Example in Spring Test

By Arvind Rai, November 18, 2019
The Spring @DirtiesContext removes the dirty ApplicationContext from context cache associated with the test. The test can modify the context such as modifying the state of singleton bean, modifying the state of an embedded database etc. Using @DirtiesContext we can clear the context cache and hence subsequent tests that request the same context will get new context. The @DirtiesContext can be used at class level as well as method level in integration tests classes.
Find the optional elements of @DirtiesContext annotation.
methodMode: It is used to configure mode when @DirtiesContext is annotated at test method level. Default value of methodMode is AFTER_METHOD.
classMode: It is used to configure mode when @DirtiesContext is annotated at class level. Default value of classMode is AFTER_CLASS.
hierarchyMode: It configures mode when context is configured via @ContextHierarchy annotation. The value can be EXHAUSTIVE and CURRENT_LEVEL. Default is EXHAUSTIVE.

Find the supported test phases.
1. BEFORE_METHOD: It is used at method level with methodMode. Current context will be removed and recreated just before this method execution.
@ExtendWith(SpringExtension.class)
public class MyAppTest {
	@DirtiesContext(methodMode = MethodMode.BEFORE_METHOD)
	@Test
	public void testMethod() {
           ------
	}
} 


2. AFTER_METHOD: It is used at method level with methodMode. Current context will be removed and recreated just after this method execution.
@ExtendWith(SpringExtension.class)
public class MyAppTest {
	@DirtiesContext(methodMode = MethodMode.AFTER_METHOD)
	@Test
	public void testMethod() {
           ------
	}
} 
3. BEFORE_EACH_TEST_METHOD: It is used at class level with classMode. Current context will be removed and recreated before every test method execution of this integration test class.
@DirtiesContext(classMode = ClassMode.BEFORE_EACH_TEST_METHOD)
@ExtendWith(SpringExtension.class)
public class MyAppTest {
   ------
} 
4. AFTER_EACH_TEST_METHOD: It is used at class level with classMode. Current context will be removed and recreated after every test method execution of this integration test class.
@DirtiesContext(classMode = ClassMode.AFTER_EACH_TEST_METHOD)
@ExtendWith(SpringExtension.class)
public class MyAppTest {
   ------
} 
5. BEFORE_CLASS: It is used at class level with classMode. In the test class hierarchy, the context will be removed and recreated before current test class execution.
@DirtiesContext(classMode = ClassMode.BEFORE_CLASS)
@ExtendWith(SpringExtension.class)
public class MyAppTest {
   ------
} 
6. AFTER_CLASS: It is used at class level with classMode. In the test class hierarchy, the context will be removed and recreated after current test class execution.
@DirtiesContext(classMode = ClassMode.AFTER_CLASS)
@ExtendWith(SpringExtension.class)
public class MyAppTest {
   ------
} 

Now we will discuss using @DirtiesContext in our integration test classes in detail.

Technologies Used

Find the technologies being used in our example.
1. Java 11
2. Spring 5.2.0.RELEASE
3. Spring Boot 2.2.0.RELEASE
4. JUnit 5
5. Maven 3.5.2
6. Eclipse 2018-09

Example: methodMode

Here we will create @DirtiesContext example using its methodMode element. We have already discussed that at method level we can use BEFORE_METHOD and AFTER_METHOD. Find the example to use them in our integration test class.
AppConfig.java
package com.concretepage;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration
@ComponentScan("com.concretepage")
public class AppConfig {
} 
StudentRepository.java
package com.concretepage;
import java.util.ArrayList;
import java.util.List;
import org.springframework.stereotype.Component;

@Component
public class StudentRepository {
     private List<String> students = new ArrayList<>();
     public void addStudent(String name) {
    	 students.add(name); 
     }
     public List<String> getAllStudents() {
    	 return students;
     }     
     public void printAllStudents() {
    	 System.out.println(students);
     }
} 
MyAppTest1.java
package com.concretepage;
import static org.junit.jupiter.api.Assertions.assertEquals;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.annotation.DirtiesContext.MethodMode;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;

@ExtendWith(SpringExtension.class)
@ContextConfiguration(classes = AppConfig.class)
public class MyAppTest1 {
	@Autowired
	private StudentRepository studentRepository;

	@Test
	public void testMethod1() {
		studentRepository.addStudent("Krishna");
		studentRepository.printAllStudents(); //[Krishna]
		assertEquals(1, studentRepository.getAllStudents().size());
	}

	@Test
	public void testMethod2() {
		studentRepository.addStudent("Mahesh");
		studentRepository.printAllStudents(); //[Krishna, Mahesh]
		assertEquals(2, studentRepository.getAllStudents().size());
	}
	
	@DirtiesContext(methodMode = MethodMode.BEFORE_METHOD)
	@Test
	public void testMethod3() {
		studentRepository.addStudent("Shiva");
		studentRepository.printAllStudents(); //[Shiva]
		assertEquals(1, studentRepository.getAllStudents().size());
	}
	
	@DirtiesContext(methodMode = MethodMode.AFTER_METHOD)
	@Test
	public void testMethod4() {
		studentRepository.addStudent("Vishnu");
		studentRepository.printAllStudents(); //[Shiva, Vishnu]
		assertEquals(2, studentRepository.getAllStudents().size());
	}
	
	@Test
	public void testMethod5() {
		studentRepository.printAllStudents(); //[]
		assertEquals(0, studentRepository.getAllStudents().size());
	}	
} 
Each test method of our above test class will result in SUCCESS. Find the print screen of JUnit test output. In case of testMethod3(), context will be cleared before execution of this method. In case of testMethod4(), context will be cleared after execution of this method. Find the print screen of JUnit test output.
@DirtiesContext Example in Spring Test

Example: classMode

Here we will create @DirtiesContext examples using its classMode element. The classMode can be assigned the values as BEFORE_EACH_TEST_METHOD, AFTER_EACH_TEST_METHOD, BEFORE_CLASS and AFTER_CLASS.
Find the example for BEFORE_EACH_TEST_METHOD.
MyAppTest2.java
package com.concretepage;
import static org.junit.jupiter.api.Assertions.assertEquals;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.annotation.DirtiesContext.ClassMode;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;

@DirtiesContext(classMode = ClassMode.BEFORE_EACH_TEST_METHOD)
@ExtendWith(SpringExtension.class)
@ContextConfiguration(classes = AppConfig.class)
public class MyAppTest2 {
	@Autowired
	private StudentRepository studentRepository;
	
	@Test
	public void testMethod1() {
		studentRepository.addStudent("Krishna");
		studentRepository.addStudent("Mahesh");
		studentRepository.printAllStudents(); //[Krishna, Mahesh]
		assertEquals(2, studentRepository.getAllStudents().size());
	}

	@Test
	public void testMethod2() {
		studentRepository.addStudent("Seeta");
		studentRepository.addStudent("Geeta");
		studentRepository.printAllStudents(); //[Seeta, Geeta]
		assertEquals(2, studentRepository.getAllStudents().size());
	}
} 
Context will be cleared before executing each test method of the above test class. The result will be SUCCESS for all above test methods in our above example.
Now find the example for AFTER_EACH_TEST_METHOD.
MyAppTest3.java
package com.concretepage;
import static org.junit.jupiter.api.Assertions.assertEquals;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.annotation.DirtiesContext.ClassMode;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;

@DirtiesContext(classMode = ClassMode.AFTER_EACH_TEST_METHOD)
@ExtendWith(SpringExtension.class)
@ContextConfiguration(classes = AppConfig.class)
public class MyAppTest3 {
	@Autowired
	private StudentRepository studentRepository;

	@Test
	public void testMethod1() {
		studentRepository.addStudent("Narendra");
		studentRepository.addStudent("Amit");
		studentRepository.printAllStudents(); //[Narendra, Amit]
		assertEquals(2, studentRepository.getAllStudents().size());
	}

	@Test
	public void testMethod2() {
		studentRepository.addStudent("Uddhav");
		studentRepository.addStudent("Rahul");
		studentRepository.printAllStudents(); //[Uddhav, Rahul]
		assertEquals(2, studentRepository.getAllStudents().size());
	}
	
	@Test
	public void testMethod3() {
		studentRepository.printAllStudents(); //[]
		assertEquals(0, studentRepository.getAllStudents().size());
	}	
} 
Context will be cleared after executing each test method of the above test class. The result will be SUCCESS for all above test methods in our above example.

Example: hierarchyMode

Here we will create @DirtiesContext examples using its hierarchyMode element. It is used to clear context when a context is configured as part of a hierarchy via @ContextHierarchy. The hierarchyMode can be assigned the values as CURRENT_LEVEL and EXHAUSTIVE.
Definition of CURRENT_LEVEL from Spring doc:
"The ApplicationContext for the current level in the context hierarchy and all contexts in subhierarchies of the current level will be removed from the context cache and closed."
Definition of EXHAUSTIVE from Spring doc:
"The context cache will be cleared using an exhaustive algorithm that includes not only the CURRENT_LEVEL but also all other context hierarchies that share an ancestor context common to the current test."

Now let us discuss the @DirtiesContext examples with hierarchyMode.
CityConfig.java
package com.concretepage;
import java.util.ArrayList;
import java.util.List;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class CityConfig {
	@Bean("city")
	public List<String> city() {
		return new ArrayList<>();
	}
} 
BookConfig.java
package com.concretepage;
import java.util.ArrayList;
import java.util.List;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class BookConfig {
	@Bean("book")	
	public List<String> book() {
		return new ArrayList<>();
	}
} 
Find the integration class that is using CURRENT_LEVEL.
MyAppTest4.java
package com.concretepage;
import static org.junit.jupiter.api.Assertions.assertEquals;
import java.util.List;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.annotation.DirtiesContext.HierarchyMode;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.ContextHierarchy;
import org.springframework.test.context.junit.jupiter.SpringExtension;

@ContextHierarchy({
    @ContextConfiguration(classes = CityConfig.class),
    @ContextConfiguration(classes = BookConfig.class)
})
@ExtendWith(SpringExtension.class)
public class MyAppTest4 {
	@Autowired
	@Qualifier("city")
	protected List<String> cityList;
	
	@Autowired
	@Qualifier("book")
	protected List<String> bookList;	

	@DirtiesContext(hierarchyMode = HierarchyMode.CURRENT_LEVEL)	
	@Test
	public void testMethod1() {
		cityList.add("Varanasi");
		bookList.add("Ramayana");
		assertEquals(1, cityList.size());
		assertEquals(1, cityList.size());
	}
	
	@Test
	public void testMethod2() {
		assertEquals(1, cityList.size());
	}
	
	@Test
	public void testMethod3() {
		assertEquals(0, bookList.size());
	}		
} 
All the test methods of the above class will result in SUCCESS.
Now find the integration class that is using EXHAUSTIVE.
MyAppTest5.java
package com.concretepage;
import static org.junit.jupiter.api.Assertions.assertEquals;
import java.util.List;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.annotation.DirtiesContext.HierarchyMode;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.ContextHierarchy;
import org.springframework.test.context.junit.jupiter.SpringExtension;

@ContextHierarchy({
    @ContextConfiguration(classes = CityConfig.class),
    @ContextConfiguration(classes = BookConfig.class)
})
@ExtendWith(SpringExtension.class)
public class MyAppTest5 {
	@Autowired
	@Qualifier("city")
	protected List<String> cityList;
	
	@Autowired
	@Qualifier("book")
	protected List<String> bookList;	

	@DirtiesContext(hierarchyMode = HierarchyMode.EXHAUSTIVE)	
	@Test
	public void testMethod1() {
		cityList.add("Pyayag");
		bookList.add("Mahabharat");
		assertEquals(1, cityList.size());
		assertEquals(1, cityList.size());
	}
	
	@Test
	public void testMethod2() {
		assertEquals(0, cityList.size());
	}
	
	@Test
	public void testMethod3() {
		assertEquals(0, bookList.size());
	}		
} 
All the test methods of the above class will result in SUCCESS.

References

Spring Testing: @DirtiesContext
Spring doc: DirtiesContext

Download Source Code

POSTED BY
ARVIND RAI
ARVIND RAI
LEARN MORE








©2024 concretepage.com | Privacy Policy | Contact Us