Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JUnit Multithreaded Test with Spring Transactions

I am running a JUnit integration test with an in-memory H2 database configured like so:

@Bean
public DataSource dataSource() {

    try {
        SimpleDriverDataSource simpleDriverDataSource = new SimpleDriverDataSource();
        simpleDriverDataSource.setDriverClass((Class<? extends Driver>) ClassUtils.forName("org.h2.Driver", this.getClass().getClassLoader()));
        simpleDriverDataSource.setUrl("jdbc:h2:file:testdb;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=false;MVCC=true;FILE_LOCK=NO;mv_store=false");
        simpleDriverDataSource.setUsername("sa");
        simpleDriverDataSource.setPassword("");

        return simpleDriverDataSource;
    } catch(ClassNotFoundException e) {
        throw new IllegalStateException(e.getMessage());
    }

}

The test makes a call to a service method. This service method uses an executor service to fork the processing. Before the call to the service class, the test method inserts some data into the database, and this data can be read using the service (via JPA repository calls) up until the task is submitted to the executor service. However, when the task is run, it is unable to read the data that was persisted previously.

How can I make the forked thread see the persisted data?

Note: This is only failing for the Unit test, it is working fine at runtime.

like image 229
Nick Avatar asked Apr 10 '16 19:04

Nick


People also ask

Is JUnit multithreaded?

4.1 concurrent-junitConcurrent-junit library helps the users to test the methods for multi threading. It will create threads for testing methods. By default, number of threads created by this library is 4, but we can set the desired number of threads.

Why is testing multithreaded concurrent code so difficult?

Testing a multithreaded application is more difficult than testing a single-threaded application because defects are often timing-related and more difficult to reproduce. Existing code often requires significant re-architecting to take advantage of multithreading and multicontexting.

Does spring run tests in parallel?

Starting with JUnit 4, tests can be run in parallel to gain speed for larger suites. The problem was concurrent test execution was not fully supported by the Spring TestContext Framework prior to Spring 5. In this quick article, we'll show how to use Spring 5 to run our tests in Spring projects concurrently.


1 Answers

Assuming that your test is @Transactional, what you see is the expected behaviour as you (usually) don't want the changes made by a test to be visible to other tests/threads, and possibly, rolled back at the end of the test.

A simple way to work around this problem is to annotate the @Test method like this

@Test
@Transactional(propagation = Propagation.NEVER)
public void testYourLogics() { /* ... */ }

I've been able to reproduce tour behaviour without the propagation = Propagation.NEVER and to see it solved with it.

Please note that you will have to restore the database to a clean state manually after running the test, or the changes made by that test, will be visible to the other tests

like image 150
snovelli Avatar answered Nov 15 '22 03:11

snovelli