Failure running Spring-Boot + Camel tests in batch - spring-boot

For my Kotlin application with Spring-Boot 2.7.0 and Apache Camel 3.17.0, I am running into a rather surprising issue: I have a set of JUnit 5 test cases that individually run fine (using mvn test -DTest="MyTest"); but when run in batch via mvn test or in IntelliJ IDEA, some test cases fail with org.apache.camel.FailedToCreateRouteException... because of Cannot set tracer on a started CamelContext.
The funny thing is, that these test cases do not have tracing enabled. My test setup looks like the following for most of the tests:
#CamelSpringBootTest
#SpringBootTest(
classes = [TestApplication::class],
properties = ["camel.springboot.java-routes-include-pattern=**/SProcessingTestRoute"]
)
#TestConstructor(autowireMode = TestConstructor.AutowireMode.ALL)
#UseAdviceWith
internal class ProcessingTest(
val template: FluentProducerTemplate,
#Value("classpath:test-resource") private val TestResource: Resource,
val camelContext: CamelContext
) {
#EndpointInject("mock:result")
lateinit var resultMock: MockEndpoint
#Test
fun `test my route`() {
AdviceWith.adviceWith(camelContext, "processing-route") { route ->
route.weaveAddLast().to("mock:result")
}
resultMock.expectedCount = 1
camelContext.start()
// ...
// here comes the actual test
}
}
There are a couple of tests where I do not advice routes; i.e., these test cases do not have the #AdviceWith annotation, and these test cases do not fail during the batch run.
Debugging this issue is hard; therefore, I would highly appreciate any pointers, hints, or hypothesis for potential causes, and ideas on what to try to narrow down the problem!

You probably need a fresh camel context for each test. Try adding #DirtiesContext to each test class. If that doesn't work, add it to each test method.

Related

writing load tests for junit springboot application

I have written the following junit test.
#SpringBootTest
public class abc {
#Autowired
private ActorService actorService;
#Test
#Transactional
public void testCorrectIdHandlingForNewInsertedActors() {
int elementsInDb = 0;
for (Actor a : this.actorService.findAll()) {
elementsInDb++;
}
Actor actor = this.actorService.saveAndSetId(new Actor());
assertEquals(elementsInDb + 1, actor.getId());
}
}
Now I want to write some load tests for performance testing but I don't know which tools I can use within my spring application. I am using gradle as my build tool. Any tutorial will be appreciated.
PS: I have already tried zerocode but does not work for me
You have some useful features out of the box such as #RepeatedTest and #Timeout (see the JUnit 5 annotations reference here) which respectively allow you to repeat a specific test method n times and set a maximum time limit before a test will fail automatically.
Other than that, for more complete and meaningful load testing you should consider relying on a full-fledged solution such as Apache JMeter or Gatling, rather than unit tests.

Previous integration test consumes current test queue message

So I am trying to use some queues in my project(rabbitmq). I decided to create simple publisher/receiver integration tests.
So I ve made simple sender
#Component
public class QueueSender {
...
public void sendMessage(#RequestParam String message) {
rabbitTemplate.convertAndSend(queue, message);
}
}
and corresponding test
#SpringBootTest(classes = QueueSender.class, webEnvironment = SpringBootTest.WebEnvironment.NONE)
#Import(RabbitAutoConfiguration.class)
class QueueSenderTest {
... --docker rabbitmq instance is one for all tests.
#Test
void shouldSendMessageToQueue() {
String message = "hello world";
queueSender.sendMessage(message);
Object response = rabbitTemplate.receiveAndConvert(queue, MAX_TIMEOUT_MILLIS);
assertEquals(message, response);
}
}
and simple receiver
#Component
public class QueueListener {
#RabbitListener(queuesToDeclare = #Queue("${queues.listener}"))
public void listener(Message object) {
System.out.println("received " + object);
}
}
and corresponding test
#SpringBootTest(classes = QueueListener.class, webEnvironment = SpringBootTest.WebEnvironment.NONE)
#Import(RabbitAutoConfiguration.class)
class QueueListenerTest {
... --docker rabbitmq instance is one for all tests.
#Test
void shouldFireListenerOnNewMessageOnQueue() {
String message = "hello world";
ArgumentCaptor<Message> argument = ArgumentCaptor.forClass(Message.class);
rabbitTemplate.convertAndSend(queue, message);
verify(queueListener, timeout(MAX_TIMEOUT_MILLIS)).listener(argument.capture());
assertEquals(message, new String(argument.getValue().getBody()));
}
}
queues configuration is placed in application.properties
queues:
listener: "listen-queue-name"
sender: "sender-queue-name"
Everything works fine till I will not to try use same queue for both tests and run them at once
queues:
listener: "listen-queue-name"
sender: "listen-queue-name"
In such case always SenderTest fails.
So when I am debuging SenderTest it seems like spring context of previous test consumes message as debugger stops in QueueListener which should not be even in context of QueueSender test -.-
Funny fact that moving one of those test classes to another package fix the problem, so probalby SenderTest is fired so fast that RabbitListener from previous test is still registered.
DirtiesContext works aswell but I dont want to use it.
Any ideas?
from the spring documentation
Spring’s test framework caches application contexts between tests.
Therefore, as long as your tests share the same configuration (no
matter how it is discovered), the potentially time-consuming process
of loading the context happens only once.
So thats your problem.
Not exactly an answer to your problem ,but more of a suggestion.
You could try using Test Containers for RabbitMQ.So essentially your test case would
1--Start RabbitMq Test Container, which will create the Queue.
2--Perform your Tests
3--Destroy the Test Container.
All of this is should be pretty quick.
This will ensure all test classes are using independent queues and hence no
possibility of any conflicts.
This in my view should be a more cleaner approach to testing.
Test Container:
https://www.testcontainers.org/modules/rabbitmq/
I think I had a related problem.
Setup
Integration test, using rabbitmq testcontainer (shared, singleton approach)
SpringBootTest, JUnit5
Test class with a nested class per 'scenario', each with several test methods
The test sends out events and then verifies that the module's event handlers do their job. I use approval testing for asserting the expected state of the resulting new or changed JPA entities, backed by MySQL (also in testcontainer)
rabbitmq test bindings are configured using application-test.yml; it imports the main application.yml and then overrides cloud/stream rabbit and bindings such that events sent by my test event sender arrive at the production code event handlers.
Problem
When running just my integration test class, such as from IDE, all is good.
BUT: when running all of the module's tests in the same JVM - whether from IDE or by Maven, then the preceding integration tests seem to interfere with the later-running. In my experience, each event handler is invoked only the first time within my integration test, when other integration test classes already ran in the same JVM. All subsequent events are not reaching their handlers.
What helped
Override the binding group name to be unique within the module's testing. This ensures that the handlers under test receive the events even when some ghosts (?) are still listening on the same channel.
This can be done in various ways (such as overriding by profiles), but the simplest is: add the overriding property to the class SpringBootTest annotation, e.g.:
#SpringBootTest(properties = "spring.cloud.stream.bindings.myEventHandler-in-0.group=myUnique123EventTestGroup-v1")
public class MyEventConsumersIntegrationTest {
FYI, the binding section in my application-test.yml looks something like this:
### In all testing:
spring:
config:
# application-test.yml is read INSTEAD of application.yml, hence need to import the latter explicitly:
import: classpath:application.yml
---
### Overrides for my module:
spring:
config:
activate:
on-profile: mymodule
cloud:
stream:
function:
definition: myEventHandler
rabbit:
bindings:
myEventHandler-in-0:
bindings:
myTestEventSender-out-0:
destination: myEventChannel
binder: rabbit
myEventHandler-in-0:
destination: myEventChannel
group: myEventGroup-v1 # Override the group value for each integration test class

How to run multiple tests with Spring Boot

With Spring Boot 1.5, how could I run multiple tests which are in different classes?
E.g.
I have `Service1` tests in `Service1test.java`;
I have `Service2` tests in `Service2test.java`;
I shall need to run both in one go.
What I have done is as follows:
In the main class
#RunWith(Suite.class)
#Suite.SuiteClasses({
PostServiceTest.class,
UserServiceTest.class
})
public class DataApplicationTests {
#Test
public void contextLoads() {
}
}
In the PostServiceTest I have
#RunWith(SpringRunner.class)
#SpringBootTest
#Transactional
public class PostServiceTest {
#Autowired
IPostService postService;
#Before
public void initiate() {
System.out.println("Initiating the before steps");
}
#Test
public void testFindPosts() {
List<Post> posts= postService.findPosts();
Assert.assertNotNull("failure - expected Not Null", posts);
}
}
The second class, UserServiceTest has similar structure.
When I run the DataApplicationTests, it runs both the classes.
I will assume you are using IntelliJ, but the same stuff apply for all the IDEs.
Gradle and Maven have got a standarized project structure, that means all Test classes positioned inside the 'test-root' will be ran on either mvn test (to just test), or while you build (to check wether the code behaves correctly. In that case, if a test fails, build fails too).
Here's an image of a marked-green test directory on IntelliJ :
Your IDE should allow you to run specific tests, test suites or classes seperately, without the need to type out any command. IntelliJ provides some Icons on the separator column thingy (near to the line numbers) that enables you to run that specific stuff. Check out these green play buttons:
Be careful with creating test suites though. That way unless you manually configure the tests that need to be run, you will get duplicate runs because the build tools will run all the test suites independently and then all the tests! (That means that if test suite A contains test suite B and C, B and C are going to be ran 2 times: 1 each from A, and 1 each independently. The same applies for standalone test classes).

junit add more test classes dynamically per user request (rerun tests) for integration testing

I am looking for a way to start the spring context, intialize all caches and after that ask the user on the command line (cmd) what tests he want to execute.
after the tests are run the user can choose to rerun the tests or run different tests until he decide to stop the programm.
this should be based on junit as it enables us to use the same tests within different execution environments (eg. jenkins build, ...)
is there a framework that support something like this or any other adwise how to implement this?
while(true) {
userInput = parseUserInputFromConsole();
if (userWantToExit(userInput)) {
break;
} else {
JunitResult = runJunitTetsBasedOnUserInput(userInput);
generateTestRunReport(JunitResult);
}
}
additional, one test exists of more then one step, but the steps should be reusable among tests. any idea how to implement this?
You can do this by using Spring #ActiveProfiles annotation, you need to basically set which tests are applicable for which run like below:
#ContextConfiguration
#ActiveProfiles({"dev", "integration"})
public class DeveloperIntegrationTests {
// class body...
}
You can look at here

Functional test in Spring: changes in database not visible in test

I have integration test (which runs under Jetty) where I open page (by using Selenium) and check that record about this activity was added to database (HSQL). But it does not work -- JPA (Hiberante) adds record (I see it in logs) but when I execute SELECT query there no records at all.
Test case:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = {
"classpath:spring/DispatcherServletContext.xml"
})
#TransactionConfiguration(defaultRollback = false)
public class WhenUserOpenNotExistingPage
extends WhenUserAtAnyPage<NotFoundErrorPage> {
private final String currentUrl;
#Autowired
private SuspiciousActivityDao suspiciousActivities;
private String generateRandomUrl() {
return String.format(
"/tests/page-does-not-exists-%s.htm",
RandomStringUtils.randomNumeric(5)
);
}
public WhenUserOpenNotExistingPage() {
currentUrl = generateRandomUrl();
page.open(currentUrl);
}
#Test
#Transactional(readOnly = true)
public void incidentShouldBeLoggedToDatabase() {
SuspiciousActivity activity =
suspiciousActivities.findByPage(currentUrl);
assertNotNull(activity);
}
}
Also WhenUserOpenNotExistingPage() (constructorr) called twice (and I don't know why it happens and probably is the root of my problem).
Can you help me?
Thanks in advance!
I assume you are adding something to the database in the test case and the same database is being used by your application running on Jetty. If your database uses any isolation level above read uncommited, the changes you made in the test case won't be visible until that test case finishes. This is because you database code joins the transaction that was created when the test was starting.
By default this test transaction is rolled back after the test finishes, so the changes are visible within the current test (transaction), but aren't visible outside (by different threads/connections) and are rolled back. You are changing the default behaviour by using defaultRollback = false attribute, but this only means that changes you made in one test aren't visible by the web application (different database connection), but will be visible in subsequent test (after commit). Not really useful.
You have few options:
Get rid of Spring transactional test support. This means Spring won't create a new transaction every time you start a test and won't do commit/rollback. Now it is up to you when to start transaction and commit it before actually starting SElenium test.
This can easily be done by replacing #TransactionConfiguration with:
#TestExecutionListeners({DependencyInjectionTestExecutionListener.class, DirtiesContextTestExecutionListener.class})
this will override default test execution listeners, removing TransactionalTestExecutionListener from the stack
You might run setup code from different thread or with propagation REQUIRES_NEW
Finally, you should consider doing setup outside of JUnit. Maybe you can do the setup from the application itself?
As for the constructor - new instance of JUnit test class is created per test. The authors of JUnit claim it makes the test more predictable and stateless (no dependencies between test) by cleaning up the test class prior to running every test. In practice, especially with integration tests, this is more a pain than a advantage.
By the way if you do some database manipulation in the test and commit the changes, watch out for test dependencies. The order in which tests are executed is not guaranteed in JUnit. This means that changes in one test shouldn't affect other tests.

Resources