В качестве основной СУБД в моем проекте используется 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); } } |
Короче говоря, ваши тесты теперь могут проверять содержимое БД. При этом можно дергать методы служб и менять содержимое таблиц точно так же, как это происходит в коде приложения.