why is my spring-cloud-stream test configuration also starting up rabbitMQ listeners in another class? - spring

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.

Related

Connections are not closed and piling up when running #SpringBootTest classes

We have Spring Boot integration tests, and keep writing new ones regularly.
I noticed database connections have been piling up: the more tests I run, the higher the connection peak to my PostgreSQL instance.
It reached a point where there are more than 300 connections requested by Spring Boot when running all tests, and it started failing the build (our max_connection is set to 300).
After some research, it came to my understanding that connections are not being released after tests have been run, because of Spring Boot test: if context is not explicitly destroyed, connections are not closed.
I find it quite weird, but tried using #DirtiesContext to prove a point, on all of our test classes, it indeed fixed the issue in a sense that it avoided peaks (no more than 30 connections at once, not piling up to 300 like before) but since this annotation forces context recreation before each test class, the build got much slower and I find it not very satisfactory to need to recreate a Spring context every time just to make sure connections are closed properly.
Data source is a HikariDataSource, configured using a configuration class.
Another workaround I found is to change maximum pool size for Hikari. I set it to something lower than the default value of 10 (I'm not sure it's useful to reserve 10 connections for each test class).
This change effectively lowers the total number of connections when I run all tests but they are still piling up (only lower!)
I think I'm missing something, how can I ensure that connections are closed after each test class? There has to be a better way than #DirtiesContext, I just can't find it. Thanks for your help.
It turns out that context was recreated almost with every test class because I was extensively using #MockBean annotation in my tests. Since it affects Spring context, each #MockBean/No MockBean combination in different test classes counts as a different context, i.e.:
Test class 1: bean MyService is a MockBean, MyOtherService is not
Test class 2: bean MyService is a MockBean, MyOtherService is also a MockBean
Test class 3: none of these two beans is a MockBean
In such case, a new Spring context will created for each class because the bean configuration is different, resulting in an increasing number of connections to the datasource.
To (partially) solve this, I looked for patterns in the beans combinations of my test classes and created a new class I called TestMockBeans.
Its sole purpose is to declare as many MockBeans and/or SpyBeans as possible to re-use in similar test configurations. I extend corresponding test classes with TestMockBeans, and then, because they share this similar setup, Spring identifies their contexts as similar and does not recreate a new one for every test class.
As you can guess, not all of my tests throughout the Spring boot app share the same need for Mockbeans (or absence of Mockbeans) so it's only a partial solution, but I hope it will help someone experiencing the same issue to mitigate it.

Camel integration spring test without #DirtiesContext

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?

Access Spring beans from a Junit Suite

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.

What are TestExecutionListeners, and what do they do?

As far as I understand, TestExecutionListeners act like #BeforeClass methods in JUnit. What I don't understand is why I need to use DependencyInjectionTestExecutionListener, TransactionalTestExecutionListener and DirtiesContextTestExecutionListener to use DbUnitTestExecutionListener.
Normally without DbUnit, I can create and populate the database. Why suddenly do I need to use these listeners to do some CRUD for my database?
TestExecutionListeners provide various types of functionality to tests running in the Spring TestContext Framework.
If you are interested in what a particular listener does, the best way to find out is to read the Javadoc for the respective class. In addition, the Testing chapter of the Spring reference manual goes into detail about how to use each of the listeners and what they do.
In your particular case, if you're not using #DirtiesContext, then you don't need to use the DirtiesContextTestExecutionListener. As for DependencyInjectionTestExecutionListener and TransactionalTestExecutionListener, you will likely need them to inject dependencies into your test (e.g., via #Autowired, #Inject, #Resource, etc.) and for transactional tests (i.e., tests annotated with #Transactional).
Note as well that the aforementioned listeners are enabled by default. So if you've been using the Spring TestContext Framework without any custom listeners like the one for DbUnit, then you just never realized that the listeners existed. The section on TestExecutionListener configuration in the reference manual should also help clarify things. Note, however, that some features like merging and auto-detection of default listeners are only available in Spring Framework 4.1+.
Regards,
Sam (author of the Spring TestContext Framework)

Integration test without mocking the connection to the other application

I have two applications. I did integration unit tests for one of these applications, but the services that call the other application are mocked up (instead of injecting the real service, I inject another one which is mocked up).
Is there a possible way to make a real connection to the other application without having to mock it up.
A simple example would be really helpful.
Thanks in advance!
just inject the real services and do your integration test. The issue is to make sure that everything that everything that needs to be injection can be injected. Lets call your services foo and bar where foo depends on bar. if bar depends on something in the application server then starting it up during a unit might be a problem, since you are not running the app in the application server.
Integration testing is important and valuable, but it requires some careful thought to setup. The way that I have managed to setup integration testing in my application is to use spring profiles to seperate the combinations of configuration. For example I have profiles called.
production
development
container
standalone
So that way you can have a test that is launched with the proper profile that setups all the correct beans to be injected like so.
#ActiveProfile(profiles={"deveolpment","standalone"})
#RunWith ... etc other spring annotations to configure a test
public class SomeJunitTest {
}
Using profiles makes it very easy to have fine grained control over which set of beans are configured for each test.
Also for integartion testing I have found TestNG to be much easier to use that JUnit because it has features that make writing integration tests easier.

Resources