I am writing tests for a Spring Integration project, and I am running into something strange : I've read about how Spring caches the context between tests and how we can force to clean the cache with #DirtiesContext annotation. However, I'm not able to explain the behavior I observe, and it makes me think it's maybe a bug...
I have 2 different tests :
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = { "classpath:myInterface-core.xml",
"classpath:myInterface-datasource-test.xml"})
public class PropertyConfigurerTest {
#Test
public void shouldResolvePropertyForOutPutFile(){
}
}
(it does nothing, simply loads the context, intentionnaly)
And another one, more complex with actual tests in it (skipping them in below snippet) :
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = {
"classpath:myInterface-core.xml",
"classpath:myInterface-rmi.xml",
"classpath:myInterface-datasource-test.xml"})
public class MontranMessagesFlowTest {
...
}
As you can see, these 2 tests don't load exactly the same config : second test loads one extra config file that is not required for first one.
When I run these 2 tests one after the other, second test is failing : in a nutshell, the goal of the test is to insert 2 rows in inMemory DB, start my Spring Integration flow and assert with a listener (inside a jms:listener-container) that I've received 2 JMS messages on the outbound side. I see in debug mode that actually the 2 messages don't go to same listener so I get one message instead of the 2 I expect. Somehow, the fact that I'm loading some elements of the context in first test (even if I don't do anything with them) has an impact on the second test.
I have found 2 different workarounds :
adding #DirtiesContext(classMode=ClassMode.AFTER_CLASS) on my first test.
modify the list of Spring files that I load in my first test, so that it matches exactly the one defined in the second test.
But still, I don't understand the rationale, and it looks like a bug to me.
I am using Spring Test 4.1.4.RELEASE. I've put the minimum code necessary in a separate project to be able to reproduce. I can share it if required.
Does anybody have an explanation for this ? Bug or not ?
Thanks
Vincent
#M. Deinum is correct in his comment.
For what it's worth, in Spring Integration framework tests themselves, we have started adding #DirtiesContext to all tests, to ensure any active components (such as inbound message-driven adapters) are always stopped after the tests complete.
This also has a performance/memory usage improvement for large test suites.
Related
I created a Test class:
void comp() throws IOException{
Foo foo=new Foo()
String file=readFile("xyz.txt")
Model model=foo.generate(file)
Assert.assertEquals(model.getMapA().size(),0);
Assert.assertEquals(model.getMapB().size(),0);
Assert.assertEquals(model.getListC().size(),10);
}
Is this a good approach to writing the Junit test? I mean all the asserts inside one method. An alternate approach I could think of is to have a setup method that will generate the model class and a separate method for checking the size of each attribute. The class Foo is a controller class in a spring boot application.
I am using Junit5. I also saw that Hamcrest is popular, but the last release was on Jul 09, 2012 Which raises a question should we use it or not
The main problem in your test scenario is when the first assertion fails you do not see results of next assertions, I would recommend to start using AssertJ assertions, in that case your assertions would look like (very basic example though):
Model model=foo.generate(file);
SoftAssertions.assertSoftly(softly -> {
softly.assertThat(model.getMapA()).isEmpty();
softly.assertThat(model.getMapB()).isEmpty();
softly.assertThat(model.getListC()).hasSize(10);
});
I am converting a spring boot application to use webflux by making the endpoints reactive one at a time. Most of the (Spock) tests worked fine, but the ones where I autowire the JPA repository in the test to insert data do not. The repository is always empty when the resource (that we're testing) reads from it. I have tens of tests like this with different repositories and they all have same issue.
Here is an example of a test (the resource we're testing just does findById and returns the example from the repository):
#SpringBootTest
#AutoConfigureWebTestClient(timeout = "60000")
#Transactional
class PaymentControllerIntegrationTest extends Specification {
#Autowired
WebTestClient client
#Autowired
PaymentRepository repo
def "GET /example/{id} returns correct example"() {
given:
def needle = new Example(id: 1L)
def haystack = repo.saveAll([needle, new Example(id: 2L), new Example(id: 3L)])
when:
def response = client.get().uri(EXAMPLE_URL, [id: needle.id.toString()]).exchange()
then:
response.expectStatus().isOk()
response.returnResult(ExampleResponse.class).getResponseBody().blockLast().id == needle.id
}
When I put a breakpoint in the controller and do findAll() the repository is always empty.
I have tried using TestEntityManager.persistAndFlush instead
I have tried using repo.saveAllAndFlush instead
I have tried autowiring ApplicationContext and then building WebTestClient from that, but I never got the autowiring to work
My best guess currently is that the test is setup incorrectly (application context?) so the repository in the test is not the same as the repository in the application.
When using the TestWebClient (or the TestRestTemplate) you are actually issuing a real HTTP request to your started server. This request is handled in a different thread and as such uses a new transaction.
Your test is transactional as well but the data hasn't been committed, another transaction can only read committed data (or you need to set the isolation level to READ_UNCOMMITTED but that is probably not something you should or want to do).
When using MockMvc you are replacing the actual container with a mocked instance and it uses a MockHttpServletRequest etc. and executes in the same thread (and thus reuse the same transaction and can see the data).
To solve, make your test not transactional and cleanup the data afterwards. This will however impact the performance of your tests (as committing and then deleting is slower as rolling back a transaction after the test).
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
I am using Spring and JUnit to write some integration tests for my DAO. I set up my test data in the beginning of the test method, and then test my DAO methods later in the same test method. The problem is that if I don't flush/commit the transaction, the EntityManager returns the same instance of the entities that I have just created in my data setup - rendering my test useless as they will always pass.
E.g.
#Test
#Transactional()
public void loadTreeBasicCase() {
// creates and saved node to DB
Node n = createNode();
// test DAO
Node result = dao.lookup(n.getId());
// verify
assertThat(n, equalTo(result));
}
One way is to expose commit() and/or flush() methods in my DAO. But I would prefer not to do that because in production code, this almost never needs to happen (let EntityManager do it's thing). Is there a way to configure this via annotations or in Spring config? I am using Spring, JPA2 with Hibernate.
You can set the defaultRollback attribute on #Transactional to reset things between tests. This doesn't sound like what you are asking for, just throwing it out there first.
Within the test, the entity manager is behaving correctly. You want to inject different behavior for testing to "disconnect" the setup from the rest of the test. One thing I did in some tests was to call flush on the entity manager directly from the test. I only had to do it a few times, but it was valuable in those cases. I did it in the test (not the DAO) so as to not provide a method on the DAO that I don't want people calling.
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.