Previous integration test consumes current test queue message - spring

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

Related

Failure running Spring-Boot + Camel tests in batch

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.

How to inspect database when running with spring-db-unit

We run our tests with spring-db-unit, the forked and maintained one.
The tests run just fine and can access and modify the data. But when I was debugging some tests I realized that the database stays empty all the time! My assumption is that the runner creates a transaction and rolls it back at the end of the test instead of commiting.
How can I inspect the data or persist it in the database at the end (or anytime, really!) of a test?
The setup:
Our config is follows the suggested setup from the gibthub readme:
#TestExecutionListeners(
inheritListeners = false,
value = {
DependencyInjectionTestExecutionListener.class,
DirtiesContextTestExecutionListener.class,
TransactionalTestExecutionListener.class,
DbUnitTestExecutionListener.class,
WithSecurityContextTestExecutionListener.class
})
#DatabaseSetup(value = "/testdata/dbunit/base_test_dataset.xml", type = DatabaseOperation.CLEAN_INSERT)
#SpringApplicationConfiguration(classes = {
DBUnitTestConfig.class,
})
#DbUnitConfiguration(databaseConnection = "dbUnitConnection")
public class ReviewServiceTest {
#Test
#WithUserDetails(TEST_COCKPIT_1)
public void getUserVersionReviews() throws Exception {
// when in a debug point here, the DB is empty.
}
}
The database is PostgreSQL 11.4. The DBUnitTestConfig class above just configures the PostgresqlDataTypeFactory of the DatabaseConfigBean
Switching to TransactionDbUnitTestExecutionListener instead of TransactionalTestExecutionListener and DbUnitTestExecutionListener didn't help.
I tried it with DatabaseOperation.INSERT instead of the default one and moved the #DatabaseSetupannotation onto test methods and setUp methods, without success.
We are using the following dependencies. It's not latest and the furthest I was able upgrade for now :
com.github.ppodgorsek:spring-test-dbunit-core:5.2.0
org.dbunit:dbunit:2.6.0
junit:junit:4.12
org.springframework.boot:spring-boot-starter-parent:1.3.8.RELEASE
OpenJDK 1.8
What confuses me that I couldn't find anyone complaining or configuration options for it, so I must be missing something obivious!
Rolling back transactions isn't spring-test-dbunit fault. It works with and without transactions. The roll back is the default behavior of the TransactionalTestExecutionListener.
Citing the javadoc of the class:
By default, test transactions
will be automatically rolled back after completion of the
test; however, transactional commit and rollback behavior can be
configured declaratively via the #Rollback annotation
at the class level and at the method level.
The solution is to put #org.springframework.test.annotation.Rollback(false) on the test class or method.

Multiple testfiles and MockRestServiceServer, expecting calls from other testfile

I've build a service with two endpoints, and I want to cover both endpoints with integration tests. To prevent these integrationtests from reaching other services, I'm using the MockRestServiceServer class to mock calls and responses to other HTTP services.
TestOperationA:
#RunWith(SpringRunner.class)
#SpringBootTest
#ActiveProfiles({"integration"})
#ComponentScan(basePackages = "nl.xyz")
public class OperationAIntegrationTest {
MockRestServiceServer mockServer;
#Autowired
RestTemplate restTemplate;
#Autowired
OperationA operationA;
#Before
public void setup() {
this.mockServer = MockRestServiceServer.bindTo(restTemplate).bufferContent().ignoreExpectOrder(true).build();
this.mockServer.reset();
}
#After
public void finish() {
// Verify all method calls are run after the testcase.
this.mockServer.verify();
this.mockServer.reset();
}
And then testcases contain stuff like:
this.mockServer.expect(requestTo(ENDPOINT_OAUTH))
.andExpect(method(HttpMethod.POST))
.andRespond(withSuccess(objectMapper.writeValueAsString(oAuthToken), MediaType.APPLICATION_JSON));
I do the same for OperationBIntegrationTest. This includes the binding to restTemplate!
Now the problem is that if I run all testcases seperately, everything succeeds. If I run all the testcases from OperationA ór OperationB, they all succeed. But when I run all the testcases so the integrationtests from OperationA and OperationB are executed in sequence, the testcases from OperationB fail. Even though I see that Spring Boot gets started anew when the testing framework jumps to the second testing file.
I'm thinking that the MockRestServiceServer does not get cleaned up or I'm doing something wrong with the binding to RestTemplate. I tried the .reset() and .verify() combinations by placing them in #Before and #After, but with no effect. Does anybody know why this is happening?
Apparently, some stuff was happening in the background causing certain variables and methods not being updated anymore since a previous test already updated it. When I don't dirty the application context (by having more MockBean's for example) then everything is allright.
So the ones adjusting the values in the background should be marked dirty.

Can I exclude logging in JUnit tests on ZK controller using Log4j and not look for a file

I need some help on logging and unit testing. The class under test is a Zk GenericForwardComposer and i want to exclude concrete logging and logging configuration from the test. Im following a kind of TDD and have a test failure because of logging. Ive posted the class under test and the test. My test doesnt have any configuration for log4j because I want as pure a unit test as possible and as simple as possible.
The test failure:
log4j:ERROR setFile(null,true) call failed.
java.io.FileNotFoundException: /log/t2-console.log (No such file or directory)
at java.io.FileOutputStream.open(Native Method)
at java.io.FileOutputStream.<init>(FileOutputStream.java:212)
at java.io.FileOutputStream.<init>(FileOutputStream.java:136)
at org.apache.log4j.FileAppender.setFile(FileAppender.java:294)
at org.apache.log4j.RollingFileAppender.setFile(RollingFileAppender.java:207)
at org.apache.log4j.FileAppender.activateOptions(FileAppender.java:165)
at org.apache.log4j.config.PropertySetter.activate(PropertySetter.java:307)
at org.apache.log4j.xml.DOMConfigurator.parseAppender(DOMConfigurator.java:295)
at org.apache.log4j.xml.DOMConfigurator.findAppenderByName(DOMConfigurator.java:176)
at org. apache.log4j.xml.DOMConfigurator.findAppenderByReference(DOMConfigurator.java:191)
at org.apache.log4j.xml.DOMConfigurator.parseChildrenOfLoggerElement(DOMConfigurator.java:523)
at org.apache.log4j.xml.DOMConfigurator.parseCategory(DOMConfigurator.java:436)
at org.apache.log4j.xml.DOMConfigurator.parse(DOMConfigurator.java:1004)
at org.apache.log4j.xml.DOMConfigurator.doConfigure(DOMConfigurator.java:872)
at org.apache.log4j.xml.DOMConfigurator.doConfigure(DOMConfigurator.java:778)
at org.apache.log4j.helpers.OptionConverter.selectAndConfigure(OptionConverter.java:526)
at org.apache.log4j.LogManager.<clinit>(LogManager.java:127)
at org.apache.log4j.Logger.getLogger(Logger.java:117)
at com.t2.integration.controller.IntegrationSearchController.<clinit>(IntegrationSearchController.java:60)
at com.t2.integration.controller.IntegrationSearchControllerTest.doesSomeCalling(IntegrationSearchControllerTest.java:14)
I haven't configured the unit test for Log4j and I want to follow Red-Green-Refactor. I think I could handle the logging call in the test but want to find a way to exclude logging entirely if that's possible.
public class IntegrationSearchControllerTest {
#Test
public void doesSomeCalling() {
IntegrationSearchController searchController = new IntegrationSearchController();
}
}
I don't want any ZK context or ZK integration testing components to leak into my unit tests. And I want the tests to be as simple as possible. Is it AOP, interfaces, dependency injection or refactoring?
The class under test:
package ...
import org.apache.log4j.Logger;
public class IntegrationSearchController extends IntegrationBaseController {
private static final Logger LOGGER = Logger.getLogger(IntegrationSearchController.class);
...
The controller is ZK managed
As your Logger is static (normal) you will need to use a mock framework that can mock statics. This is much more difficult to do (from a framework perspective as it usually involves manipulating byte code). Anyway it's been done and you have options.
Here's how it would look using PowerMock with Mockito:
#RunWith(PowerMockRunner.class)
#PrepareForTest(Logger.class)
public class TestIntegrationSearchController {
#Before
public void initMockLogger() {
mockStatic(Logger.class);
when(Logger.getLogger(any(Class.class))).thenReturn(mock(Logger.class));
}
#Test
public void test() {
IntegrationSearchController controller = new IntegrationSearchController();
// controller.LOGGER is a Mockito mocked Logger
}
}
Note: you don't need to set up mocking in an #Before but I find it reads easier.
Now.. all that said, I think you're solving the wrong problem here. Rather than mocking logging in every test, consider alternatives that obviate the need. For example, you could use a different Log4J configuration in tests which logs to STDOUT.

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