3

I am writing tests for Service that uses several data Jpa repositories. The problem is that some repositories use a lot of native queries with MySQL specific functions such as str_to_date(). So when I tried to test the service's method using H2 I got an error saying that H2 doesn't recognize function. I have tried using H2 in MySQL mode, but got the same error.

here mariaDB4j was proposed as a work-around. I have added dependency into Maven

<dependency>
    <groupId>ch.vorburger.mariaDB4j</groupId>
    <artifactId>mariaDB4j</artifactId>
    <version>2.3.0</version>
    <scope>test</scope>
</dependency>

But getting IllegalStateException : Failed to replace DataSource with an embedded database for tests. If you want an embedded database please put a supported one on the classpath or tune the replace attribute of @AutoConfigureTestDatabase.

My Test file looks this way:

@RunWith(SpringRunner.class)
@DataJpaTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.ANY)
public class TestPay {

    @TestConfiguration
    static class PaymentServiceTestContextConfiguration {
        @Bean
        public PaymentService paymentService(){
            return new PaymentService();
        }
    }

    @Autowired
    private PaymentService paymentService;
    @Autowired
    private TarifRepository firstRepository;
    @Autowired
    private BuildingRepository secondRepository;
    @Autowired
    private ApartmentRepository thirdRepository;

    /* Test cases here*/
}

The project is build with Annotation driven Spring Boot.

FirePapaya
  • 509
  • 5
  • 21
  • A simple comment on testing, IMO unit testing that includes external sources could become brittle. The purpose of unit testing is to make sure your code is doing what it's supposed to do. If you can achieve that by mocking your database instead of using an in memory one I would do that. Obviously it's up to your team and org what they prefer this is just my 2 cents. – Matt Feb 06 '19 at 20:16
  • IMHO it makes a lot of sense to test as much integration as possible in *JUnit* tests, not only unit tests in the strict sense. But really, this is a "Two people - three opinions" topic, and we should stop the discussion here... ;-) – oberlies Feb 08 '19 at 12:25

2 Answers2

3

I build the following class that I reuse in every integration test that requires database access to mariadb. It could probably be improved (and I'd be happy for suggestions), but it works so far:

@RunWith(SpringRunner.class)
@SpringBootTest
@TestPropertySource(locations="classpath:application-junit.properties")
@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS) //otherwise mariadb is not cleaned up between tests
public abstract class MyIntegrationTest {

    private static MariaDB4jSpringService DB;

    @BeforeClass
    public static void init() throws ManagedProcessException {
        DB = new MariaDB4jSpringService();
        DB.setDefaultPort(1234);
        DB.start();
        DB.getDB().createDB("yourtables");
        DB.getDB().source("schema.sql"); // init scripts from /src/test/resources/schema.sql
    }

    @AfterClass
    public static void cleanup() {
        if (DB != null) DB.stop();
    }
}

application-junit.properties:

spring.datasource.url=jdbc:mariadb://localhost:1234/yourtables
spring.datasource.username=root
spring.datasource.password=
membersound
  • 81,582
  • 193
  • 585
  • 1,120
2

It sounds like you need to explicitly declare your DataSource for tests. In the case of h2, there is likely already a datasource bean declared by a spring test dependency, but there may not be an off-the-shelf one provided by ch.vorburger.mariaDB4j.

Here's an example of an embedded MariaDB DataSource that I stole from elsewhere on the internet

import ch.vorburger.mariadb4j.DBConfigurationBuilder
import ch.vorburger.mariadb4j.springframework.MariaDB4jSpringService
import org.springframework.beans.factory.annotation.Value
import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.context.annotation.Profile

import javax.sql.DataSource

@Configuration
@Profile(['local', 'integrationTest'])
class EmbeddedMariaDbConfig {

    @Bean
    MariaDB4jSpringService mariaDB4jSpringService() {
        new MariaDB4jSpringService()
    }

    @Bean
    DataSource dataSource(MariaDB4jSpringService mariaDB4jSpringService,
                          @Value('${app.mariaDB4j.databaseName}') String databaseName,
                          @Value('${spring.datasource.username}') String datasourceUsername,
                          @Value('${spring.datasource.password}') String datasourcePassword,
                          @Value('${spring.datasource.driver-class-name}') String datasourceDriver) {
        //Create our database with default root user and no password
        mariaDB4jSpringService.getDB().createDB(databaseName)

        DBConfigurationBuilder config = mariaDB4jSpringService.getConfiguration()

        DataSourceBuilder
                .create()
                .username(datasourceUsername)
                .password(datasourcePassword)
                .url(config.getURL(databaseName))
                .driverClassName(datasourceDriver)
                .build();
    }
}
tjarratt
  • 1,692
  • 9
  • 17