15

I have a bunch of JUnit tests that all function individually. Each one is a true standalone unit test - single class under test. No contexts are required. I can run them all individually or all together either in Eclipse or via maven / surefire-plugin.

I have since added a new Integration test which leverages the Spring Context, etc and uses the SpringJUnit4ClassRunner. As soon as I add this test to my suite, any test cases run after this class fail.

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = IntegrationTestConfiguration.class)
@DirtiesContext(classMode=ClassMode.AFTER_EACH_TEST_METHOD)
@ActiveProfiles("test")
public class ImportServiceIntegrationTest {
   ...
}

I'm not sure this has tremendous value, but I am posting my configuration class here as well:

@EnableAutoConfiguration(exclude = { WebMvcAutoConfiguration.class,
        DispatcherServletAutoConfiguration.class,
        EmbeddedServletContainerAutoConfiguration.class,
        WebSocketAutoConfiguration.class })
@ComponentScan(basePackages = "com.rtc.synchronize",
        excludeFilters = @ComponentScan.Filter(type = FilterType.REGEX, pattern="com\\.rtc\\.synchronize\\.config\\.AppConfig"))
@EnableJpaRepositories("com.util.veracode.rtc.synchronize")
@EntityScan("com.util.veracode.rtc.synchronize")
public class IntegrationTestConfiguration {
}

If my actual @Configuration classes will be of use, I can post those as well, although for brevity, I have avoided them (I'm not entirely sure how useful they would be).

I suspect that there is something maintained in the JVM (some static data) after the test class is terminated.

I am using Spring Cache annotations with the following config:

@Configuration
@EnableCaching(mode=AdviceMode.ASPECTJ)
public class CacheConfig extends CachingConfigurerSupport{

    /**
     * EhCache configuration.  Used to minimize calls to Veracode
     * 
     * @return
     */
    @Bean(destroyMethod="shutdown")
    public net.sf.ehcache.CacheManager ehCacheManager() {
        ...
        ...
    }
        ...
}

As soon as my integration test class finishes, my subsequent test throws the following error:

java.lang.IllegalStateException: The workItems Cache is not alive (STATUS_SHUTDOWN)
        at net.sf.ehcache.Cache$CacheStatus.checkAlive(Cache.java:4097)
        at net.sf.ehcache.Cache.checkStatus(Cache.java:2788)
        at net.sf.ehcache.Cache.get(Cache.java:1744)
        at org.springframework.cache.ehcache.EhCacheCache.get(EhCacheCache.java:65)
        at org.springframework.cache.interceptor.AbstractCacheInvoker.doGet(AbstractCacheInvoker.java:68)
        at org.springframework.cache.interceptor.CacheAspectSupport.findInCaches(CacheAspectSupport.java:461)
        at org.springframework.cache.interceptor.CacheAspectSupport.findCachedItem(CacheAspectSupport.java:432)
        at org.springframework.cache.interceptor.CacheAspectSupport.execute(CacheAspectSupport.java:333)
        at org.springframework.cache.interceptor.CacheAspectSupport.execute(CacheAspectSupport.java:299)
        at org.springframework.cache.aspectj.AbstractCacheAspect.ajc$around$org_springframework_cache_aspectj_AbstractCacheAspect$1$2bc714b5(AbstractCacheAspect.aj:74)
        at com.synchronize.repository.rtc.WorkItemRepositoryImpl.findById(WorkItemRepositoryImpl.java:192)
        at com.synchronize.repository.rtc.WorkItemRepositoryImpl.findById(WorkItemRepositoryImpl.java:192)
        at com.synchronize.repository.rtc.WorkItemRepositoryImpl.getState(WorkItemRepositoryImpl.java:179)
        at com.synchronize.repository.rtc.WorkItemRepositoryImplTest.testGetState(WorkItemRepositoryImplTest.java:178)

So it is fairly clear to me that Spring is not cleaning something up after it is done (my subsequent class doesn't even load the Spring context - it is a plain vanilla Junit test!).

If I add <resueForks>false</reuseForks> to my surefire-plugin definition, all tests pass, but I am not happy about that solution/workaround. It slows down the build and it isn't respected in Eclipse - that is I simply run a JUnit test runner against an entire project in a single shot without it failing.

Do I have to do something special to ensure that Spring clears itself out of the JVM once the test case is complete? Why do I have some Spring configuraiton hanging around post my integration test?

Eric B.
  • 23,425
  • 50
  • 169
  • 316
  • What is in you IntegrationTestConfiguration? When your tests are done Spring context would be destroyed and I suspect along with it your EhCache. – codesalsa Nov 06 '15 at 22:02
  • @codesalsa I agree - the context should in theory be destroyed and all associated Spring beans. So why is the Spring cache interceptor even being called in the first place? I've updated my post with the additional config data. – Eric B. Nov 07 '15 at 02:18
  • Was the problem happening before you integrated Spring? If so, are you using a setup or beforeClass for the setup before the test? – Beefster Nov 07 '15 at 05:32
  • I noticed in my test suites that some AspectJ based implementations (I observed this for Spring-Security, but maybe there is a similar problem with @Cache) have static fields that reference some spring bean. And this reference gets not updated when the profile or even the complete spring test context changes. My Workarround is to put them in an other director `src/test/javaSecurity` and then configure this directory explicite. And I also use a naming pattern, in order to run them in a separete surefire process – Ralph Nov 07 '15 at 11:03
  • If this is a solution for you, then I will write a more detailed answer. – Ralph Nov 07 '15 at 11:08
  • @Beefster - Not the problem only occurs if I run the Spring test. Without the Spring integrated test, everything runs fine/as expected. – Eric B. Nov 07 '15 at 13:15
  • @Ralph - I suspect the same thing. I took a quick look at the woven classes and see a bunch of static methods/etc, so I suspect there is some static field that is being set and persisted in the JVM across the tests. I presume you just configured your Surefire plugin to exclude your javaSecurity tests on the first execution, and only run them in a second one? Using false forces the plugin to spawn a new JVM for each test, so I see it as a workaround too. I'm just not very happy about it. – Eric B. Nov 07 '15 at 13:18
  • @Eric B. - Use two test executions - see my answer. – Ralph Nov 07 '15 at 14:13
  • My tests were failing together but successful alone. I was missing @DirtiesContext(classMode=ClassMode.AFTER_EACH_TEST_METHOD) . Thanks for that hint :) – supernova Aug 29 '16 at 00:42
  • Hello, @EricB. Have you find out any better solution after almost 6 years? I'm facing this problem right now. :( – Samir Ribeiro Feb 16 '21 at 14:13
  • 1
    @samir.sales Sorry - if memory serves, I ended up delivering the test suite with reuseForks and have since moved on to different projects. – Eric B. Feb 16 '21 at 18:22

1 Answers1

4

I noticed in my test suites that some AspectJ based implementations have static fields that reference some spring bean, and this reference gets not updated when the profile or even the complete spring test context changes (I observed this for Spring-Security, but maybe there is a similar problem with @Cache)

My Workaround is to group the test cases by used spring configuration in different directories (for example src/test/javaSecurity) and also use a naming pattern to separate them. - The separated directories/source folders are used for eclipse, so can I click on the directory root folder and instruct eclipse to run all tests in this source folder. - The naming pattern is used for executing the test with maven (because surefire has the feature to select the executed tests by class naming pattern, but not by root folder)

  • Directories/Patterns
    • src/test/java, pattern: *Test - contains tests with a spring configuration without spring-security
    • src/test/javaSecurity, pattern: *SecurityTest Spring tests that need an enabled spring security

maven

<plugin>
    <groupId>org.codehaus.mojo</groupId>
    <artifactId>build-helper-maven-plugin</artifactId>
    <executions>
        <execution>
            <id>add-security-test-source</id>
            <phase>generate-sources</phase>
            <goals>
                <goal>add-test-source</goal>
            </goals>
            <configuration>
                <sources>
                    <source>${basedir}/src/test/javaSecurity</source>
                </sources>
            </configuration>
        </execution>
    </executions>
</plugin>


<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-surefire-plugin</artifactId>
    <executions>
        <execution>
            <id>normal-test</id>
            <phase>test</phase>
            <goals>
                <goal>test</goal>
            </goals>
            <configuration>
                <excludes>
                    <exclude>**/Abstract*.java</exclude>
                    <exclude>**/*SecurityTest.java</exclude>
                </excludes>
                <includes>
                    <include>**/*Test.java</include>
                    <include>**/*Tests.java</include>
                </includes>
                <reportsDirectory>${project.build.directory}/surefire-reports/normaltest</reportsDirectory>
            </configuration>
        </execution>            
        <execution>
            <id>security-tests</id>
            <phase>test</phase>
            <goals>
                <goal>test</goal>
            </goals>
            <configuration>
                <excludes>
                    <exclude>**/Abstract*.java</exclude>
                </excludes>
                <includes>
                    <include>**/*SecurityTest.java</include>
                </includes>
                <reportsDirectory>${project.build.directory}/surefire-reports/security-test</reportsDirectory>
            </configuration>
        </execution>
    </executions>
    <configuration>
        <!-- just needed to prevent some bugs -->
        <excludes />
        <includes>
            <include>nothing</include>
        </includes>
        <reportsDirectory>${project.build.directory}/surefire-reports/nothing</reportsDirectory>
    </configuration>
</plugin>
Ralph
  • 118,862
  • 56
  • 287
  • 383