Java spring test database

Интеграционные тесты баз данных с помощью Spring Boot и Testcontainers

С помощью Spring Data JPA можно легко создавать запросы к БД и тестировать их с помощью встроенной базы данных H2.

Но иногда тестирование на реальной базе данных намного более полезно, особенно если мы используем запросы, привязанные к конкретной реализации БД.

В этом руководстве мы покажем, как использовать Testcontainers для интеграционного тестирования со Spring Data JPA и базой данных PostgreSQL.

В предыдущей статье мы создали несколько запросов к БД, используя в основном аннотацию @Query, которые мы сейчас и протестируем.

2. Конфигурация

Чтобы использовать в наших тестах базу данных PostgreSQL, мы должны добавить зависимость Testcontainers только тестов и драйвер PostgreSQL в наш файл pom.xml:

 org.testcontainers postgresql 1.10.6 test  org.postgresql postgresql 42.2.5 

Также создадим в каталоге ресурсов тестирования файл application.properties, в котором мы зададим для Spring использование нужного класса драйвера, а также создание и удаление схемы БД при каждом запуске теста:

spring.datasource.driver-class-name=org.postgresql.Driver spring.jpa.hibernate.ddl-auto=create-drop

3. Единичный тест

Чтобы начать использовать экземпляр PostgreSQL в классе с одним тестом, необходимо создать определение контейнера, а затем использовать его параметры для установления соединения:

@RunWith(SpringRunner.class) @SpringBootTest @ContextConfiguration(initializers = ) public class UserRepositoryTCIntegrationTest extends UserRepositoryCommonIntegrationTests < @ClassRule public static PostgreSQLContainer postgreSQLContainer = new PostgreSQLContainer("postgres:11.1") .withDatabaseName("integration-tests-db") .withUsername("sa") .withPassword("sa"); static class Initializer implements ApplicationContextInitializer< public void initialize(ConfigurableApplicationContext configurableApplicationContext) < TestPropertyValues.of( "spring.datasource.url=" + postgreSQLContainer.getJdbcUrl(), "spring.datasource.username=" + postgreSQLContainer.getUsername(), "spring.datasource.password java">@Modifying @Query("update User u set u.status = :status where u.name = :name") int updateUserSetStatusForName(@Param("status") Integer status, @Param("name") String name); @Modifying @Query(value = "UPDATE Users u SET u.status = ? WHERE u.name = ?", nativeQuery = true) int updateUserSetStatusForNameNative(Integer status, String name);

И протестируем в настроенной среде исполнения:

@Test @Transactional public void givenUsersInDB_WhenUpdateStatusForNameModifyingQueryAnnotationJPQL_ThenModifyMatchingUsers() < insertUsers(); int updatedUsersSize = userRepository.updateUserSetStatusForName(0, "SAMPLE"); assertThat(updatedUsersSize).isEqualTo(2); >@Test @Transactional public void givenUsersInDB_WhenUpdateStatusForNameModifyingQueryAnnotationNative_ThenModifyMatchingUsers() < insertUsers(); int updatedUsersSize = userRepository.updateUserSetStatusForNameNative(0, "SAMPLE"); assertThat(updatedUsersSize).isEqualTo(2); >private void insertUsers()

В приведенном выше сценарии первый тест заканчивается успешно, а второй выдает InvalidDataAccessResourceUsageException с сообщением:

Caused by: org.postgresql.util.PSQLException: ERROR: column "u" of relation "users" does not exist

Если бы мы запускали те же самые тесты с использованием встроенной базы данных H2, оба были бы успешно завершены, но PostgreSQL не принимает алиасы в выражении SET. Мы можем быстро поправить запрос, удалив проблемный алиас:

@Modifying @Query(value = "UPDATE Users u SET status = ? WHERE u.name = ?", nativeQuery = true) int updateUserSetStatusForNameNative(Integer status, String name);

На этот раз оба теста пройдены успешно. В этом примере мы использовали Testcontainers для выявления проблемы с нативным запросом, которая в противном случае была бы обнаружена только после перехода на рабочую базу данных. Также следует заметить, что использование запросов JPQL в целом безопаснее, поскольку Spring правильно их переводит в зависимости от используемого провайдера базы данных.

4. Общий экземпляр базы данных

В предыдущем разделе мы описали, как использовать Testcontainers в единичном тесте. В реальных кейсах хотелось бы использовать один и тот же БД-контейнер в нескольких тестах из-за относительно длительного времени запуска.

Создадим общий класс для создания контейнера базы данных, унаследовав PostgreSQLContainer и переопределив методы start () и stop ():

public class BaeldungPostgresqlContainer extends PostgreSQLContainer  < private static final String IMAGE_VERSION = "postgres:11.1"; private static BaeldungPostgresqlContainer container; private BaeldungPostgresqlContainer() < super(IMAGE_VERSION); >public static BaeldungPostgresqlContainer getInstance() < if (container == null) < container = new BaeldungPostgresqlContainer(); >return container; > @Override public void start() < super.start(); System.setProperty("DB_URL", container.getJdbcUrl()); System.setProperty("DB_USERNAME", container.getUsername()); System.setProperty("DB_PASSWORD", container.getPassword()); >@Override public void stop() < //do nothing, JVM handles shut down >>

Оставляя метод stop() пустым, мы даем возможность JVM самостоятельно обработать завершение работы контейнера. Мы также реализуем простой singleton, в котором только первый тест запускает контейнер, а каждый последующий тест использует существующий экземпляр. В методе start() мы используем System#setProperty для сохранение параметров соединения в переменные среды.

Теперь мы можем записать их в файл application.properties:

spring.datasource.url=$ spring.datasource.username=$ spring.datasource.password=$

Теперь используем наш служебный класс в определении теста:

@RunWith(SpringRunner.class) @SpringBootTest public class UserRepositoryTCAutoIntegrationTest < @ClassRule public static PostgreSQLContainer postgreSQLContainer = BaeldungPostgresqlContainer.getInstance(); // tests >

Как и в предыдущих примерах, мы применили аннотацию @ClassRule к полю с определением контейнера. Таким образом, параметры подключения DataSource заполняются правильными значениями до создания контекста Spring.

Теперь мы можем реализовать несколько тестов, используя один и тот же экземпляр базы данных, просто задав поле с аннотацией @ClassRule, созданное с помощью нашего служебного класса BaeldungPostgresqlContainer.

5. Заключение

В этой статье мы показали методы тестирования на рабочей базе данных с помощью Testcontainers.

Ещё рассмотрели примеры использования единичного теста с помощью механизма ApplicationContextInitializer из Spring, а также реализации класса для многократного использования экземпляра базы данных.

Мы также показали, как Testcontainers может помочь в выявлении проблем совместимости между несколькими поставщиками баз данных, особенно для нативных запросов.

Как всегда, полный код, используемый в этой статье, доступен на GitHub.

Источник

Spring JdbcTemplate Unit Testing

announcement - icon

As always, the writeup is super practical and based on a simple application that can work with documents with a mix of encrypted and unencrypted fields.

We rely on other people’s code in our own work. Every day.

It might be the language you’re writing in, the framework you’re building on, or some esoteric piece of software that does one thing so well you never found the need to implement it yourself.

The problem is, of course, when things fall apart in production — debugging the implementation of a 3rd party library you have no intimate knowledge of is, to say the least, tricky.

Lightrun is a new kind of debugger.

It’s one geared specifically towards real-life production environments. Using Lightrun, you can drill down into running applications, including 3rd party dependencies, with real-time logs, snapshots, and metrics.

Learn more in this quick, 5-minute Lightrun tutorial:

announcement - icon

Slow MySQL query performance is all too common. Of course it is. A good way to go is, naturally, a dedicated profiler that actually understands the ins and outs of MySQL.

The Jet Profiler was built for MySQL only, so it can do things like real-time query performance, focus on most used tables or most frequent queries, quickly identify performance issues and basically help you optimize your queries.

Critically, it has very minimal impact on your server’s performance, with most of the profiling work done separately — so it needs no server changes, agents or separate services.

Basically, you install the desktop application, connect to your MySQL server, hit the record button, and you’ll have results within minutes:

announcement - icon

DbSchema is a super-flexible database designer, which can take you from designing the DB with your team all the way to safely deploying the schema.

The way it does all of that is by using a design model, a database-independent image of the schema, which can be shared in a team using GIT and compared or deployed on to any database.

And, of course, it can be heavily visual, allowing you to interact with the database using diagrams, visually compose queries, explore the data, generate random data, import data or build HTML5 database reports.

Get started with Spring Data JPA through the reference Learn Spring Data JPA course:

1. Overview

Spring JdbcTemplate is a powerful tool for developers to focus on writing SQL queries and extracting results. It connects to the back-end database and executes SQL queries directly.

Therefore, we can use integration tests to make sure that we can pull data from the database properly. Also, we can write unit tests to check the correctness of the related functionalities.

In this tutorial, we’ll show how to unit test JdbcTemplate code.

2. JdbcTemplate and Running Queries

Firstly, let’s start with a data access object (DAO) class that uses JdbcTemplate:

public class EmployeeDAO < private JdbcTemplate jdbcTemplate; public void setDataSource(DataSource dataSource) < jdbcTemplate = new JdbcTemplate(dataSource); >public int getCountOfEmployees() < return jdbcTemplate.queryForObject("SELECT COUNT(*) FROM EMPLOYEE", Integer.class); >>

We dependency-inject a DataSource object into the EmployeeDAO class. Then, we create the JdbcTemplate object in the setter method. Also, we use JdbcTemplate in an example method getCountOfEmployees().

There are two ways to unit test methods that use JdbcTemplate.

We can use an in-memory database such as the H2 database as the data source for testing. However, in real-world applications, the SQL query could have complicated relationships, and we need to create complex setup scripts to test the SQL statements.

Alternatively, we can also mock the JdbcTemplate object to test the method functionality.

3. Unit Test With H2 Database

We can create a data source that connects to the H2 database and inject it into the EmployeeDAO class:

@Test public void whenInjectInMemoryDataSource_thenReturnCorrectEmployeeCount() < DataSource dataSource = new EmbeddedDatabaseBuilder().setType(EmbeddedDatabaseType.H2) .addScript("classpath:jdbc/schema.sql") .addScript("classpath:jdbc/test-data.sql") .build(); EmployeeDAO employeeDAO = new EmployeeDAO(); employeeDAO.setDataSource(dataSource); assertEquals(4, employeeDAO.getCountOfEmployees()); >

In this test, we first construct a data source on the H2 database. During the construction, we execute schema.sql to create the EMPLOYEE table:

CREATE TABLE EMPLOYEE ( ID int NOT NULL PRIMARY KEY, FIRST_NAME varchar(255), LAST_NAME varchar(255), ADDRESS varchar(255) );

Also, we run test-data.sql to add test data into the table:

INSERT INTO EMPLOYEE VALUES (1, 'James', 'Gosling', 'Canada'); INSERT INTO EMPLOYEE VALUES (2, 'Donald', 'Knuth', 'USA'); INSERT INTO EMPLOYEE VALUES (3, 'Linus', 'Torvalds', 'Finland'); INSERT INTO EMPLOYEE VALUES (4, 'Dennis', 'Ritchie', 'USA');

Then, we can inject this data source into the EmployeeDAO class and test the getCountOfEmployees method over the in-memory H2 database.

4. Unit Test With Mock Object

We can mock the JdbcTemplate object so that we don’t need to run the SQL statement on a database:

public class EmployeeDAOUnitTest < @Mock JdbcTemplate jdbcTemplate; @Test public void whenMockJdbcTemplate_thenReturnCorrectEmployeeCount() < EmployeeDAO employeeDAO = new EmployeeDAO(); ReflectionTestUtils.setField(employeeDAO, "jdbcTemplate", jdbcTemplate); Mockito.when(jdbcTemplate.queryForObject("SELECT COUNT(*) FROM EMPLOYEE", Integer.class)) .thenReturn(4); assertEquals(4, employeeDAO.getCountOfEmployees()); >>

In this unit test, we first declare a mock JdbcTemplate object with the @Mock annotation. Then we inject it to the EmployeeDAO object using ReflectionTestUtils. Also, we use the Mockito utility to mock the return result of the JdbcTemplate query. This allows us to test the functionality of the getCountOfEmployees method without connecting to a database.

We use an exact match on the SQL statement string when we mock the JdbcTemplate query. In real-world applications, we may create complex SQL strings, and it is hard to do an exact match. Therefore, we can also use the anyString() method to bypass the string check:

Mockito.when(jdbcTemplate.queryForObject(Mockito.anyString(), Mockito.eq(Integer.class))) .thenReturn(3); assertEquals(3, employeeDAO.getCountOfEmployees());

5. Spring Boot @JdbcTest

Finally, if we’re using Spring Boot, there is an annotation we can use to bootstrap a test with an H2 database and a JdbcTemplate bean: @JdbcTest.

Let’s create a test class with this annotation:

@JdbcTest @Sql() class EmployeeDAOIntegrationTest < @Autowired private JdbcTemplate jdbcTemplate; @Test void whenInjectInMemoryDataSource_thenReturnCorrectEmployeeCount() < EmployeeDAO employeeDAO = new EmployeeDAO(); employeeDAO.setJdbcTemplate(jdbcTemplate); assertEquals(4, employeeDAO.getCountOfEmployees()); >>

We can also note the presence of the @Sql annotation which allows us to specify the SQL files to run before the test.

6. Conclusion

In this tutorial, we showed multiple ways to unit test JdbcTemplate.

As always, the source code for the article is available over on GitHub.

announcement - icon

Slow MySQL query performance is all too common. Of course it is. A good way to go is, naturally, a dedicated profiler that actually understands the ins and outs of MySQL.

The Jet Profiler was built for MySQL only, so it can do things like real-time query performance, focus on most used tables or most frequent queries, quickly identify performance issues and basically help you optimize your queries.

Critically, it has very minimal impact on your server’s performance, with most of the profiling work done separately — so it needs no server changes, agents or separate services.

Basically, you install the desktop application, connect to your MySQL server, hit the record button, and you’ll have results within minutes:

Источник

Читайте также:  Which php ini command line
Оцените статью