Интеграционные тесты с помощью dbunit и h2

В качестве основной СУБД в моем проекте используется postgres, но в тестах мы будем использовать in-memory БД h2. В этом случае нам не потребуется отдельной БД и все данные будут в оперативной памяти. После тестов не надо что-то подчищать.

Необходимые зависимости:

        <!-- assertj-core нужен только для assert в тестах, вы можете использовать и аналоги-->
        <dependency>
            <groupId>org.assertj</groupId>
            <artifactId>assertj-core</artifactId>
            <version>3.2.0</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>4.3.5.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.dbunit</groupId>
            <artifactId>dbunit</artifactId>
            <version>2.5.3</version>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <artifactId>junit</artifactId>
                    <groupId>junit</groupId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>com.github.springtestdbunit</groupId>
            <artifactId>spring-test-dbunit</artifactId>
            <version>1.3.0</version>
            <scope>test</scope>
        </dependency>
        <!-- in memory DB -->
        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
            <version>1.4.194</version>
            <scope>test</scope>
        </dependency>

Проперти файл с настройками БД:

jdbc.driverClassName=org.h2.Driver
jdbc.url=jdbc:h2:mem:<название БД>;INIT=RUNSCRIPT FROM 'classpath:init.sql';DB_CLOSE_DELAY=-1
hibernate.dialect=org.hibernate.dialect.H2Dialect
hibernate.hbm2ddl.auto=create
 
hibernate.show_sql=true
hibernate.format_sql=true
hibernate.connection.release_mode=on_close
...
(специфичные настройки для c3p0 и ehcache)

Обратите внимание на строку jdbc.url: в ней указан скрипт init.sql, который будет вызван перед запуском тестов.

А это тестовый конфиг:

@Configuration
@EnableJpaRepositories(basePackages = {"ru.outofrange.repo"})
@ComponentScan(value = {"ru.outofrange.service"})
@PropertySource({"hibernate_test.properties", "classpath:application.properties"})
@Import({LibraryConfig.class})
@EnableTransactionManagement
public class PersisitenceTestConfig {
@Autowired
    private Environment environment;
 
    @Bean
    public DataSource dataSource() {
        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setDriverClassName(environment.getProperty("jdbc.driverClassName"));
        dataSource.setUrl(environment.getProperty("jdbc.url"));
        dataSource.setUsername(environment.getProperty("jdbc.user"));
        dataSource.setPassword(environment.getProperty("jdbc.pass"));
        return dataSource;
    }
 
    @Bean
    public H2Connection h2Connection() throws SQLException, DatabaseUnitException {
        return new H2Connection(dataSource().getConnection(), "<schema name>");
    }
 
    @Bean
    public JpaTransactionManager transactionManager(EntityManagerFactory emf) {
        return new JpaTransactionManager(emf);
    }
 
    @Bean
    public PersistenceExceptionTranslationPostProcessor exceptionTranslation() {
        return new PersistenceExceptionTranslationPostProcessor();
    }
...
}

Содержимое скрипта init.sql:

CREATE SCHEMA IF NOT EXISTS <schema name>;
 
DROP ALIAS IF EXISTS <schema name>.custom_to_number;
CREATE ALIAS <schema name>.custom_to_number AS $$
Long customToNumber(String str) {
    return Long.valueOf(str);
}
$$;

Здесь создается функция custom_to_number, которая принимает строку (ожидается, что в ней десятичное число) в качестве параметра, и выдает это число. Эта функция рассматривалась тут: статья. Для совместимости с кодом, который будет тестироваться, нужно такое же имя схемы.

Собственно, тестирующий класс:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {PersisitenceTestConfig.class},
        loader = AnnotationConfigContextLoader.class)
@TestExecutionListeners({DependencyInjectionTestExecutionListener.class,
        DirtiesContextTestExecutionListener.class,
        TransactionalTestExecutionListener.class,
        DbUnitTestExecutionListener.class})
@DbUnitConfiguration(databaseConnection = "h2Connection")
@DatabaseSetup("classpath:claim_search_dataset.xml")
public class ClaimServiceTestIT {
    @Autowired
    ClaimRepo claimRepo;
 
    @Autowired
    ClaimService claimService;
 
    @Test
    public void testClaimSearch() {
 
    }

Из специфичных аннотаций здесь @DbUnitConfiguration(databaseConnection = «h2Connection») — указываем имя бина, который создали ранее в конфиге. И @DatabaseSetup(«classpath:claim_search_dataset.xml») — в нем описаны данные, которые будут размещены в БД.

Данные на вход dbunit такие:

<dataset>
    <TOPIC_TYPES id="1" officetype="type 1"/>
    <TOPIC_TYPES id="2" officetype="type 2"/>
    <TOPIC_TYPES id="3" officetype="type 3"/>
    <TOPIC_TYPES id="4" officetype="type 4"/>
    <TOPIC_TYPES id="5" officetype="type 5"/>
 
    <TOPICS id="1" author_id="1" type_id="1" status_id="2" version="0"/>
    <TOPICS id="2" author_id="2" type_id="3" status_id="2" version="0"/>
    <TOPICS id="3" author_id="3" type_id="2" status_id="2" version="0"/>
    <TOPICS id="20" author_id="4" type_id="4" status_id="1" version="0"/>
</dataset>

Названия таблиц не чувствительны к регистру. Структуру таблиц у меня создает сам hibernate, т.к. я указал:

hibernate.hbm2ddl.auto=create

Ну и, собственно, тестирующий класс:

import static org.assertj.core.api.Assertions.assertThat;
 
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {PersisitenceTestConfig.class},
        loader = AnnotationConfigContextLoader.class)
@TestExecutionListeners({DependencyInjectionTestExecutionListener.class,
        DirtiesContextTestExecutionListener.class,
        TransactionalTestExecutionListener.class,
        DbUnitTestExecutionListener.class})
@DbUnitConfiguration(databaseConnection = "h2Connection")
@DatabaseSetup("classpath:claim_search_dataset.xml")
public class ClaimServiceTestIT {
 
    @Autowired
    TopicRepo topicRepo;
 
    @Autowired
    TopicService TopicService;
 
    @Test
    public void testTopicSearch() {
        Topic topic1 = topicRepo.findOne(1l);
        Topic topic2 = topicRepo.findOne(2l);
        Topic topic20 = topicRepo.findOne20l);
        ...
        List<Topic> topiscs = topicService.findBySomeCriteria(...);
        assertThat(claims)
                .hasSize(3)
                .doesNotContainNull()
                .contains(topic1)
                .contains(topic2)
                .contains(topic20);
    }
}

Короче говоря, ваши тесты теперь могут проверять содержимое БД. При этом можно дергать методы служб и менять содержимое таблиц точно так же, как это происходит в коде приложения.

You can leave a response, or trackback from your own site.

Leave a Reply