Ben
Ben

Reputation: 361

AutoConfigureTestDatabase with multiple DataSource beans

I have two DataSources in my application, which work as expected when connecting to the databases in a live environment. However, when writing unit tests for this application, I'm encountering problems with my bean definitions.

This is my primary DataSource configuration:

@Configuration
@EnableJpaRepositories(
        basePackages = "foo.bar.repository.primary",
        entityManagerFactoryRef = "primaryEntityManager",
        transactionManagerRef = "primaryTransactionManager")
public class PrimaryDataSourceConfig {

    @Bean
    @Primary
    @ConfigurationProperties("primary.datasource")
    public DataSource primaryDataSource() {
        return DataSourceBuilder.create().build();
    }

    @Bean
    @Primary
    public LocalContainerEntityManagerFactoryBean primaryEntityManager() {

        LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
        em.setDataSource(primaryDataSource());
        em.setPackagesToScan("foo.bar.domain.entity");

        HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
        em.setJpaVendorAdapter(vendorAdapter);

        Map<String, String> properties = new HashMap<>();
        properties.put("hibernate.implicit_naming_strategy",
                "org.springframework.boot.orm.jpa.hibernate.SpringImplicitNamingStrategy");
        properties.put("hibernate.physical_naming_strategy",
                "org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy");
        em.setJpaPropertyMap(properties);

        return em;
    }

    @Bean
    @Primary
    public PlatformTransactionManager primaryTransactionManager() {

        JpaTransactionManager transactionManager = new JpaTransactionManager();
        transactionManager.setEntityManagerFactory(primaryEntityManager().getObject());
        return transactionManager;
    }
}

I have a test where all I want to do is spin up the entire application context, looking like this.

@AutoConfigureTestDatabase
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class ErpApplicationTest {

    @Test
    public void test() {
        // Application started
    }
}

However, when I run this test, I get the following error:

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'primaryEntityManager' defined in class path resource [foo/bar/primaryDataSourceConfig.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean]: Factory method 'primaryEntityManager' threw exception; nested exception is java.lang.IllegalArgumentException: No visible constructors in class org.springframework.boot.test.autoconfigure.jdbc.TestDatabaseAutoConfiguration$EmbeddedDataSourceFactoryBean

What is causing this error, and what can I do to rectify it? Flyway is able to connect to the embedded H2 instance the annotation generates and run its migrations, but the primary DataSource, alongside its EntityManager fail at creation.

Upvotes: 1

Views: 4885

Answers (1)

Ben
Ben

Reputation: 361

I resolved the issue by doing the following:

First I changed the AutoConfigureTestDatabase-annotation to not replace any DataSource:

@AutoConfigureTestDatabase(
        replace = AutoConfigureTestDatabase.Replace.NONE)

I was now experiencing an error where I would timeout against the database. Since the AutoConfig defaults to an in-memory h2, I figured the previous database connection settings were incorrect. I changed my application.properties-file to contain the following:

primary.datasource.driver-class-name=org.h2.Driver
primary.datasource.jdbc-url=jdbc:h2:~;MODE=MYSQL
primary.datasource.username=
primary.datasource.password=

secondary.datasource.driver-class-name=org.h2.Driver
secondary.datasource.jdbc-url=jdbc:h2:~;MODE=MYSQL
secondary.datasource.username=
secondary.datasource.password=

Presumably this error occurred because the auto configuration couldn't properly replace the entityManager for my custom datasource. By ensuring the DataSource wasn't replaced, and ensuring the connection url was correct, the test went through as expected.

Upvotes: 4

Related Questions