How to shutdown application context between tests
Asked Answered
R

1

11

I have 3 groups of tests: unit, integration, acceptance.

  • Latter two groups launches ApplicationContext: minimal for "integration" and full for "acceptance".
  • Both application contexts register queue subscriber.
  • Application contexts are deregistered at the end of entire test run (@RunWith(SpringRunner.class))

When I run "all tests" 2 different application contexts are started and I have duplicate queue subscribers.

I know following workarounds for this subscribers duplication:

  • never run integration and acceptance test together
  • use "acceptance" application context for "integration" tests. Downside: test run will take longer time.
  • add static registry and manually add/remove listeners. Downside: too complex and easy-to-forget

Are there any convinient way to unload application context after a group of tests?

UPDATE based on ndrone answer

  • @DirtiesContext is a perfect match
  • one more option is to limit cached ApplicationContexts count to one with spring.test.context.cache.maxSize=1

Test superclass example with dirties context

    @RunWith(SpringRunner.class)
    @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
    @TestExecutionListeners({FlywayTestExecutionListener.class, DependencyInjectionTestExecutionListener.class, DirtiesContextTestExecutionListener.class})
    @DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS)
    public abstract class AcceptanceTest {}
Rick answered 30/4, 2018 at 11:24 Comment(1)
Have you looked into @DirtyContext? javarticles.com/2016/03/…Imitation
P
0

I would highly recommend reading this article throughout: https://docs.spring.io/spring-framework/reference/testing/testcontext-framework/ctx-management/caching.html. It explains quite well how Spring attempts to cache and reuse contexts of tests running in the same JVM.

I see 2 potential approaches to solve your problem.

Option 1

You manage to setup the 2 test cases in a way that they build the same context cache key according to the rules described in the article above. In one of my projects I used @ActiveProfiles to achieve this. I basically created my own annotation to mark tests that should share the resource (in your case you could name the annotation @RegistersQueueSubscribers) and annotated that annotation with the @SpringBootTest and the @ActiveProfiles annotations. With this, all test classes annotated with that annotation will activate the corresponding profile creating a specific context cache key for Spring. Note that this still doesn't guarantee 100% that caching works, as one annotated test class could setup something else in the Spring context generating a different context key. So, this approach might help but depends on how you are setting up your tests.

Option 2

You manage the queue subscriptions outside of the Spring context. Spring's context caching mechanism is quite nice but not that easy to understand and follow what happens behind the curtains. If you have a critical resource like such a queue and you must make sure that subscriptions happen only once, then it might be better to manage this outside of Spring so that you have full control of what's going on. You could for example have your own Singleton that holds all subscriptions so that they are reused if multiple tests attempt to subscribe. Note however that this could bring other problems depending on what you're doing. What if tests are running in parallel and 2 tests compete to consume messages on the same queue? Such race conditions could make your tests unstable.

Plunder answered 5/6 at 9:49 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.