How do you reset Spring JUnit application context after a test class dirties it?

73,710

Use @DirtiesContext to force a reset. For example I have:

@ContextConfiguration(classes={BlahTestConfig.class})
@DirtiesContext(classMode = ClassMode.AFTER_EACH_TEST_METHOD)
public class SomeTest {

    @Autowired XXXX xx;
    @Autowired YYYY yy;

    @Before
    public void setUp() {
        MockitoAnnotations.initMocks(this);
        when(YYYY.newYY()).thenReturn(zz);
    }

    @Test
    public void testSomeTest() {
        XX.changeSomething("StringTest");
        XX.doSomething();
        check_for_effects();
    }

    @Test
    public void testSomeOtherTest() {
        XX.changeSomething("SomeotherString");
        XX.doSomething();
        check_for_effects();
    }

From spring docs

DirtiesContext

Indicates that the underlying Spring ApplicationContext has been dirtied (modified)as follows during the execution of a test and should be closed, regardless of whether the test passed:

  • After the current test class, when declared on a class with class mode set to AFTER_CLASS, which is the default class mode.

  • After each test method in the current test class, when declared on a class with class mode set to AFTER_EACH_TEST_METHOD.

  • After the current test, when declared on a method.

Use this annotation if a test has modified the context (for example, by replacing a bean definition). Subsequent tests are supplied a new context. [Note] Limitations of @DirtiesContext with JUnit 3.8

> In a JUnit 3.8 environment @DirtiesContext is only supported on methods and thus not at the class level.

You can use @DirtiesContext as a class-level and method-level annotation within the same 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.

@DirtiesContext
public class ContextDirtyingTests {
    // some tests that result in the Spring container being dirtied
}

@DirtiesContext(classMode = ClassMode.AFTER_EACH_TEST_METHOD)
public class ContextDirtyingTests {
    // some tests that result in the Spring container being dirtied
}

@DirtiesContext
@Test
public void testProcessWhichDirtiesAppCtx() {
    // some logic that results in the Spring container being dirtied
}

When an application context is marked dirty, it is removed from the testing framework's cache and closed; thus the underlying Spring container is rebuilt for any subsequent test that requires a context with the same set of resource locations.

Share:
73,710
Dave
Author by

Dave

Updated on June 26, 2021

Comments

  • Dave
    Dave about 3 years

    I'm using Spring 3.1.1.RELEASE, JUnit 4.8.1 and the HSQL 2.7.7 in-memory database. I have one test class annotated as

    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration({ "classpath:test-trainingSessionServiceContext.xml" })
    @DirtiesContext(classMode = ClassMode.AFTER_EACH_TEST_METHOD)
    public class TrainingSessionServiceTest  
    {
    

    The problem is, when I run "mvn clean test", it seems that all test classes run after the above class fail because the in-memory database is destroyed and not re-created. I get errors like

    org.hibernate.exception.SQLGrammarException: user lacks privilege or object not found: CB_ORGANIZATION" type="javax.persistence.PersistenceException">javax.persistence.PersistenceException:   org.hibernate.exception.SQLGrammarException: user lacks privilege or object not found: CB_ORGANIZATION
        at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1360)
        at org.hibernate.ejb.AbstractEntityManagerImpl.find(AbstractEntityManagerImpl.java:817)
        at org.hibernate.ejb.AbstractEntityManagerImpl.find(AbstractEntityManagerImpl.java:771)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
        at java.lang.reflect.Method.invoke(Method.java:597)
        at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:240)
        at $Proxy46.find(Unknown Source)
        at org.mainco.subco.organization.repo.OrganizationDaoImpl.findById(OrganizationDaoImpl.java:77)
        at org.mainco.subco.pd.repo.LinkDaoTest.createDummyLink(LinkDaoTest.java:686)
        at org.mainco.subco.pd.repo.LinkDaoTest.testSaveLink(LinkDaoTest.java:67)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
        at java.lang.reflect.Method.invoke(Method.java:597)
        at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:44)
        at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
    

    Here is how I setup the test class (run after the above class) that gives the exceptions …

    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration({ "classpath:test-context.xml" })
    public class LinkDaoTest extends AbstractTransactionalJUnit4SpringContextTests
    {
    

    Is there any way I can restore my application context to its original state before each test class is run? I don't want to make the "TrainingSessionServiceTest" class extend AbstractTransactionalJUnit4SpringContextTests. Here is the relevant part of my application context:

    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="org.hsqldb.jdbcDriver"/>
        <property name="url" value="jdbc:hsqldb:mem:pd" />
        <property name="username" value="sa" />
        <property name="password" value="" />
    </bean>
    
    <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <property name="jpaVendorAdapter">
            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"/>
        </property>
        <property name="persistenceXmlLocation" value="classpath:META-INF/test-persistence.xml"/>
        <property name="persistenceUnitName" value="testingDatabase"/>
        <property name="dataSource" ref="dataSource"/>
    </bean>
    
    <bean id="sharedEntityManager" class="org.springframework.orm.jpa.support.SharedEntityManagerBean">
       <property name="entityManagerFactory" ref="entityManagerFactory"/>
    </bean>
    
    <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
        <property name="entityManagerFactory" ref="entityManagerFactory"/>
    </bean>
    
    <tx:annotation-driven />
    
    <jdbc:embedded-database id="embedded" type="HSQL"/> 
    <jdbc:initialize-database data-source="dataSource">
        <jdbc:script location="classpath:db-test-data.sql"/>    
    </jdbc:initialize-database>  
    
  • Alexander H
    Alexander H over 10 years
    Um... doesn't @Dave's original code already use DirtiesContext, as shown above. I can't see any edits. What am I missing?
  • Yogesh Chawla
    Yogesh Chawla over 8 years
    Depending on the use case, you might need to add this setting to mvn surefire plugin: <reuseForks>false</reuseForks>
  • spyro
    spyro about 3 years
    Having the same problem (with H2). Old context is not fully shutdown so the port of the embedded H2 is still block resulting in a broken new context