0

I have a Spring Boot REST project with multiple @SpringBootTest JUnit test cases.

The project uses Spring Cache with Cache2K. There is a factory bean that creates a CacheManager with a cache.

@Bean
public CacheManager cache2kCacheManager() {
    SpringCache2kCacheManager cache2kCacheManager = new SpringCache2kCacheManager()
            .defaultSetup(b -> b.entryCapacity(3).expireAfterWrite(3, TimeUnit.SECONDS).disableMonitoring(false));
    Function<Cache2kBuilder<?, ?>, Cache2kBuilder<?, ?>> campaignCacheBuilder;
    campaignCacheBuilder = x -> x.name("campaigns-cache")
            .entryCapacity(5));
    cache2kCacheManager.addCaches(campaignCacheBuilder);
    return cache2kCacheManager;
}

All the test cases run successfully when run in the IDE. The application also runs when launched in the IDE. However when I run mvn clean install on the project, the test cases fail due to Cache object creation error.

[ERROR] testCacheReadAndWrite  Time elapsed: 0 s  <<< ERROR!
java.lang.IllegalStateException: Failed to load ApplicationContext
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'cache2kCacheManager' defined in class path resource [com/demos/cachedemo/cache/configuration/Cache2KConfiguration.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.cache.CacheManager]: Factory method 'cache2kCacheManager' threw exception; nested exception is java.lang.IllegalStateException: Cache already created: 'campaigns-cache'
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.cache.CacheManager]: Factory method 'cache2kCacheManager' threw exception; nested exception is java.lang.IllegalStateException: Cache already created: 'campaigns-cache'
Caused by: java.lang.IllegalStateException: Cache already created: 'campaigns-cache'

I tried removing the test cases with error, but others start failing with the same exception. It appears that the context is being reused/shared. I have already added @DirtiesContext to the test cases, but that does not fix the issue.

Can anyone help me with this issue?

Update 1: The project was created using start.spring.io and has the default build plugin in pom.xml.

<build>
  <plugins>
    <plugin>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-maven-plugin</artifactId>
      <configuration>
        <excludes>
          <exclude>
          <groupId>org.projectlombok</groupId>
          <artifactId>lombok</artifactId>
          </exclude>
        </excludes>
      </configuration>
    </plugin>
  </plugins>
</build>
Shankar
  • 2,625
  • 3
  • 25
  • 49
  • 1
    I'm not an expert on cache2k, but `SpringCache2kCacheManager` constructor takes a `name` parameter, perhaps randomizing the name or using the current thread name would solve the issue? – crizzis Apr 03 '21 at 11:57
  • @crizzis randomizing the cache name may work, but usually cache names are based on business functions, so it does not look good. Also it would be good to know why the problem happens, since there seems to be some sharing between the application contexts. I am not sure if this is cache2k issue or SpringBootTest. – Shankar Apr 04 '21 at 01:49
  • 1
    I would assume this is an issue with cache2k, akin to trying to create the same database twice, but again, I'm no expert. You could create a property in `application.yaml` for the cache manager name and override it for tests using `ApplicationContextInitializer`. Or, perhaps cache2k itself has an option to skip creating a cache if it already exists. Again, you could enable it for tests only, though you might still get concurrency issues – crizzis Apr 04 '21 at 07:19

1 Answers1

1

The issue was due to the Maven Surefire tests running in parallel. This answer provided a solution which solved the problem.

The project was created using start.spring.io and had default build plugins (I have edited the question with the previous build configuration).

I added the following Surefire configuration to limit the parallel runs.

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-surefire-plugin</artifactId>
            <configuration>
                <reuseForks>false</reuseForks>
                <forkCount>1</forkCount>
            </configuration>
        </plugin>
    </plugins>
</build>

If there is a better solution, please post it.

Shankar
  • 2,625
  • 3
  • 25
  • 49