en Lopes
en Lopes

Reputation: 2153

SpringBoot: Configuring Spring DataSource for Tests

I have a SpringBoot app.

I have created this test:

@ContextConfiguration(classes={TestConfig.class})
@RunWith(SpringRunner.class)
@SpringBootTest
public class SuncionServiceITTest {
    @Test
    public void should_Find_2() {
        // TODO
    }
}

where

@Configuration
@EnableJpaRepositories(basePackages = "com.plats.bruts.repository")
@PropertySource("local-configuration.properties")
@EnableTransactionManagement
@SpringBootApplication(exclude = {SecurityAutoConfiguration.class})
public class TestConfig {
}

and local configuration.properties:

spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.url=jdbc:h2:mem:db;DB_CLOSE_DELAY=-1
spring.datasource.username=sa
spring.datasource.password=sa

but when I run the test. I got this error:

Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'entityManagerFactory' available

I also tried with:

@EnableJpaRepositories(basePackages = "com.plats.bruts.repository", entityManagerFactoryRef="emf")

but then I have the error:

Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'emf' available

Upvotes: 5

Views: 2981

Answers (3)

Michael Ushakov
Michael Ushakov

Reputation: 2809

I prefer to use following approach (i don't like to create own bean configurator). As @svr correctly noticed (see previous answer) you don't add starter package for beans auto configure. I usually create different profiles: for local app running, for dev, prod and finally last one for tests. In my test profile (application-functests.yml) i configure all settings that needs for my functional tests (datasources, hibernate, cache and so on), i.e. my application-functests.yml of one of my projects:

spring:
  http:
    encoding:
      charset: UTF-8
      enabled: true
  profiles: functests
  jpa:
    show-sql: true
    properties:
      hibernate:
        format_sql: true
        enable_lazy_load_no_trans: true
        naming:
            physical-strategy: com.goodt.drive.orgstructure.application.utils.SnakePhysicalNamingStrategy
    hibernate:
      ddl-auto: none
    database-platform: org.hibernate.dialect.PostgreSQL9Dialect    
  datasource:
    driver-class-name: org.postgresql.Driver
    url: jdbc:postgresql://localhost:5432/monitor_service_functests
    username: developer
    password: 123
    sql-script-encoding: UTF-8
  liquibase:
    change-log: classpath:db/changelog/changelog.xml

I have only specify profile for running test, therefore all of my functional tests are using functests profile, i.e.:

@SpringBootTest
@ActiveProfiles("functests")
public class TestEventRepository extends FunctionalTestBase {
    
    @Test
    public void testGetAll() {
        Iterable<EventEntity> eventIterable = dbContext.getEventDataSource().findAll();
        Iterator<EventEntity> it = eventIterable.iterator();
        List<EventEntity> actualEvents = new ArrayList<>();
        while (it.hasNext()) {
            actualEvents.add(it.next());   
        }
        List<EventCheckData> expectedEvents = new ArrayList<>() {{
            add(new EventCheckData(1L, 1L, "body 1", 1L, 1L));
            add(new EventCheckData(2L, 2L, "body 2", 2L, 2L));
            add(new EventCheckData(3L, 3L, "body 3", 3L, 1L));
            add(new EventCheckData(4L, 1L, "body 4", 2L, 1L));
            add(new EventCheckData(5L, 2L, "body 5", 1L, 2L));
        }};
        EventSimpleChecker.check(expectedEvents, actualEvents);
    }
}

In my code example i have base test class - FunctionalTestBase which is used for interact with liquibase (toggle it to apply migrations)

Upvotes: 2

s7vr
s7vr

Reputation: 75994

Looks like you are missing below starter dependency. This starter dependency has all the necessary dependencies needed to configure the jpa repositories.

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

Upvotes: 4

user8280225
user8280225

Reputation:

This is an approach of how to configure several data sources in one application. I have tested it for spring-webmvc and graphql-java, but I think it can be useful for spring-boot as well.


Configure several data sources with spring-data-jpa

For each database we should enable JPA repositories and specify the base packages for the corresponding interfaces. Also for each database we should specify the entity manager factory and the base packages for corresponding entities, as well as the transaction manager and the data source, of course.

To do this, we'll include in our application one configuration class for data JPA and three inner classes for each database. See the Simple GraphQL implementation.

DataJpaConfig.java

package org.drakonoved.graphql;

@Configuration
@PropertySource(value = "classpath:resources/application.properties", encoding = "UTF-8")
public class DataJpaConfig {
    private final String basePackage = "org.drakonoved.graphql";

    @EnableJpaRepositories(
            basePackages = basePackage + ".repository.usersdb",
            entityManagerFactoryRef = "usersdbEntityManagerFactory",
            transactionManagerRef = "usersdbTransactionManager")
    public class UsersDBJpaConfig {
        @Bean("usersdbEntityManagerFactory")
        public LocalContainerEntityManagerFactoryBean usersDBEntityManagerFactoryBean(
                @Value("${datasource.usersdb.script}") String script) {
            return createEntityManagerFactoryBean(
                    script, "usersdb", basePackage + ".dto.usersdb");
        }

        @Bean("usersdbTransactionManager")
        public PlatformTransactionManager usersDBTransactionManager(
                @Qualifier("usersdbEntityManagerFactory")
                        LocalContainerEntityManagerFactoryBean factoryBean) {
            return new JpaTransactionManager(factoryBean.getNativeEntityManagerFactory());
        }
    }

    @EnableJpaRepositories(
            basePackages = basePackage + ".repository.rolesdb",
            entityManagerFactoryRef = "rolesdbEntityManagerFactory",
            transactionManagerRef = "rolesdbTransactionManager")
    public class RolesDBJpaConfig {
        @Bean("rolesdbEntityManagerFactory")
        public LocalContainerEntityManagerFactoryBean rolesDBEntityManagerFactoryBean(
                @Value("${datasource.rolesdb.script}") String script) {
            return createEntityManagerFactoryBean(
                    script, "rolesdb", basePackage + ".dto.rolesdb");
        }

        @Bean("rolesdbTransactionManager")
        public PlatformTransactionManager rolesDBTransactionManager(
                @Qualifier("rolesdbEntityManagerFactory")
                        LocalContainerEntityManagerFactoryBean factoryBean) {
            return new JpaTransactionManager(factoryBean.getNativeEntityManagerFactory());
        }
    }

    @EnableJpaRepositories(
            basePackages = basePackage + ".repository.usersnrolesdb",
            entityManagerFactoryRef = "usersnrolesdbEntityManagerFactory",
            transactionManagerRef = "usersnrolesdbTransactionManager")
    public class UsersNRolesDBJpaConfig {
        @Bean("usersnrolesdbEntityManagerFactory")
        public LocalContainerEntityManagerFactoryBean usersNRolesDBEntityManagerFactoryBean(
                @Value("${datasource.usersnrolesdb.script}") String script) {
            return createEntityManagerFactoryBean(
                    script, "usersnrolesdb", basePackage + ".dto.usersnrolesdb");
        }

        @Bean("usersnrolesdbTransactionManager")
        public PlatformTransactionManager usersNRolesDBTransactionManager(
                @Qualifier("usersnrolesdbEntityManagerFactory")
                        LocalContainerEntityManagerFactoryBean factoryBean) {
            return new JpaTransactionManager(factoryBean.getNativeEntityManagerFactory());
        }
    }

    //////// //////// //////// //////// //////// //////// //////// ////////

    private LocalContainerEntityManagerFactoryBean createEntityManagerFactoryBean(
            String script, String dbname, String packagesToScan) {
        var factoryBean = new LocalContainerEntityManagerFactoryBean();
        factoryBean.setDataSource(new EmbeddedDatabaseBuilder()
                .setType(EmbeddedDatabaseType.H2)
                .setName(dbname)
                .addScript(script)
                .build());
        factoryBean.setPersistenceUnitName(dbname + "EntityManagerFactory");
        factoryBean.setPackagesToScan(packagesToScan);
        factoryBean.setJpaVendorAdapter(new HibernateJpaVendorAdapter());

        var properties = new HashMap<String, Object>();
        properties.put("hibernate.hbm2ddl.auto", "none");
        properties.put("hibernate.dialect", "org.hibernate.dialect.H2Dialect");
        factoryBean.setJpaPropertyMap(properties);

        return factoryBean;
    }
}

Upvotes: 2

Related Questions