When I run an integration test of camel route processing as a standalone test, it passes. When I run all the tests, it fails with assertion errors of expected values. When I add #DirtiesContext to the abstract test class, they all pass ok.
I checked the docs and beyond the paragraph below, they do not say why #DirtiesContext is actually needed, and what goes wrong when not used.
Notice that we use #DirtiesContext on the test methods to force Spring Testing to automatically reload the CamelContext after each test method - this ensures that the tests don't clash with each other (e.g. one test method sending to an endpoint that is then reused in another test method).
The thing is that creating spring context again and again is quite time consuming. All our other non-camel integration tests pass without #DirtiesContext (using #Transactional) and we would like to continue with that.
Is there a way to put camel to pristine state without recreating the whole spring context so the test's MockEndpoints work as expected?
Related
Spring Boot always loads full components in the container when I try to run a junit test everytime.It takes more than 2 minutes to run,and that wastes me a lot of time.So,how can I avoid this?
You could and should avoid using Spring Dependency Injection in unit test, otherwise is more like an integration test then a real unit test.
You could manually instantiate your under test classes (via constructor) and mock their dependencies.
A cool library like mockito can help you with mocks.
Say I have 10 spring boot test class (annotated with #RunWith(SpringRunner.class) and #SpringBootTest)
Each test needs to launch spring container for like 10 seconds, although the container might do the same init.
So I may need 100 seconds for "mvn test".
Is there a way I can group my 10 test class into 1 suite, and let the container only start once.
So I can:
Only run the suite for "mvn test". (with proper naming for individual test class)
Optionally run individual test in IDE.
Spring uses Cache Management to cache the Application Context between tests:
By default, once loaded, the configured ApplicationContext is reused for each test. Thus, the setup cost is incurred only once per test suite, and subsequent test execution is much faster. In this context, the term “test suite” means all tests run in the same JVM — for example, all tests run from an Ant, Maven, or Gradle build for a given project or module. In the unlikely case that a test corrupts the application context and requires reloading (for example, by modifying a bean definition or the state of an application object) the TestContext framework can be configured to reload the configuration and rebuild the application context before executing the next test. (https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/testing.html#integration-testing)
So this mechanism tries to execute your integration tests on an already running Application Context if possible. As you see multiple Application Context launches, this indicates your tests somehow use a different setup e.g. different profiles active, test properties, MockBeans etc.
The Spring documentation provides an overview on which indicators it puts an Application Context in its cache: https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/testing.html#testcontext-ctx-management-caching
If you e.g. don't change any test property for your integration tests, Spring can run all of them on only one Application Context and be extremely efficient.
Another indicator for your current behaviour might be the use of #DirtiesContext which leads to a fresh Application Context after your test executes.
I was reading through the guide for using Spring Boot Test and there was a paragraph that got me confused.
“As our profiles get richer, it's tempting to swap every now and then in our integration tests. There are convenient tools to do so, like #ActiveProfiles. However, every time we pull a test with a new profile, a new ApplicationContext gets created.”
https://www.baeldung.com/spring-tests
So it assumes that if all tests are run under the same profile, there is only one ApplicationContext created — but how is it possible? I thought that all the objects are recreated for each test suite anyway. Am I missing something?
The official reference says that it's cached.
https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/testing.html#testing-ctx-management
But how does it get loaded into the JUnit runner or Spock one across multiple test suites?
What was missing in my understanding is the fact that all the test suites are run as a part of a single program, so it's easy to cache any objects that are required by all of them, including Spring context.
I'm trying to do a simple spring-cloud-stream unit test to verify the wiring between streams; basically that a handler can read from one stream and write to another. This part is working fine. The problem is that other parts of the app are being started as well; namely, a rabbitMQ listener. There is a method in another class (besides the one I'm testing) which has #RabbitListener. This is the method being called. And I do have rabbit running locally on my machine, for local dev testing. But I don't want this invoked within the test scope.
The spring-cloud-stream test docs here example has
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment= SpringBootTest.WebEnvironment.RANDOM_PORT)
I think it's the #SpringBootTest which is starting up the entire configuration, including the RabbitMQ listeners. I've removed the webEnvironment parameter, but that didn't make a difference.
For now, the workaround is to put spring.rabbitmq.listener.simple.auto-startup: false in application.yml, but that's not something I want to continue for various reasons, one of which is that I probably want to unit test that rabbitlistener at one point, albeit in a properly-limited test context.
We're using version 2.0.1 of spring-cloud-stream and spring-cloud-stream-test-support, although this seems to be a more fundamental spring configuration issue that I don't understand how to limit the context.
You can use a property placeholder in the autoStartup property
spring.rabbitmq.listener.simple.auto-startup: ${auto.start:true}
and then use a #TestPropertySource in the test case to set it to false.
I want to accomplish this - Run a background process (a Solr instance actually) that all the tests in my JUnit Suite will use.
To do this - Created a JUnit class annotated with #RunWith(Suites.class). And added a ClassRule on the Suite to start the server and stop it. Individual tests in the suite were annotated with #SpringApplicationConfiguration and #RunWith(SpringJunit4ClassRunner.class). And I also require access to some of the Beans in the Suite itself (like a spring managed settings bean). What's the best way to do this. What I tried.
Annotated individual tests with #SpringApplicationConfiguration
Had the Suite create an ApplicationContext via
SpringApplication.run and access any bean that it wants (a spring
managed Settings bean for example and use it one of ClassRules of
this Suite).
What I observed is that the ApplicationContext context gets created everytime, One for the Suite because I called SpringApplication.run and one for every test. I obviously want to avoid this and caching of the ApplicationContext between test runs also does not seem to work in this case.
So what are the best practices to handle this case.
Any suggestions/recommendations will be highly appreciated.
This is a long forgotten question, but it shown up on my google search, so I am assuming it still somehow relevant :)
I was going down on the same path but I hit so many road blocks that I decided to change my approach:
Create a JUnit runner as described here.
Change #1 to inherit SpringJUnit4ClassRunner here is an example.
Finally, annotate your Test classes with #RunWith(MyCustomRunner.class)
There you go. You can add whatever logic you need inside your runner.