«1. Обзор

Spring JdbcTemplate — это мощный инструмент, позволяющий разработчикам сосредоточиться на написании SQL-запросов и извлечении результатов. Он подключается к серверной базе данных и напрямую выполняет запросы SQL.

Поэтому мы можем использовать интеграционные тесты, чтобы убедиться, что мы можем правильно извлекать данные из базы данных. Кроме того, мы можем написать модульные тесты для проверки правильности соответствующих функций.

В этом руководстве мы покажем, как выполнить модульное тестирование кода JdbcTemplate.

2. JdbcTemplate и выполнение запросов

Во-первых, давайте начнем с класса объекта доступа к данным (DAO), который использует 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);
    }
}

Мы внедряем объект DataSource с зависимостями в класс EmployeeDAO. Затем мы создаем объект JdbcTemplate в методе установки. Кроме того, мы используем JdbcTemplate в примере метода getCountOfEmployees().

Существует два способа модульного тестирования методов, использующих JdbcTemplate.

Мы можем использовать базу данных в памяти, такую ​​как база данных H2, в качестве источника данных для тестирования. Однако в реальных приложениях запрос SQL может иметь сложные взаимосвязи, и нам необходимо создавать сложные сценарии настройки для проверки операторов SQL.

В качестве альтернативы мы также можем смоделировать объект JdbcTemplate, чтобы проверить функциональность метода.

3. Модульный тест с базой данных H2

Мы можем создать источник данных, который подключается к базе данных H2, и внедрить его в класс EmployeeDAO:

@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());
}

В этом тесте мы сначала создадим источник данных на базу данных Н2. Во время построения мы выполняем schema.sql для создания таблицы EMPLOYEE:

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

Также мы запускаем test-data.sql для добавления тестовых данных в таблицу:

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');

Затем мы можем внедрить это источник данных в класс EmployeeDAO и протестируйте метод getCountOfEmployees в базе данных H2 в памяти.

4. Модульный тест с имитацией объекта

Мы можем имитировать объект JdbcTemplate, чтобы нам не нужно было запускать оператор SQL в базе данных:

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());
    }
}

В этом модульном тесте мы сначала объявляем макет объекта JdbcTemplate с аннотацией @Mock. Затем мы внедряем его в объект EmployeeDAO с помощью ReflectionTestUtils. Кроме того, мы используем утилиту Mockito для имитации возвращаемого результата запроса JdbcTemplate. Это позволяет нам протестировать функциональность метода getCountOfEmployees без подключения к базе данных.

Мы используем точное совпадение в строке оператора SQL, когда имитируем запрос JdbcTemplate. В реальных приложениях мы можем создавать сложные строки SQL, и трудно добиться точного совпадения. Поэтому мы также можем использовать метод anyString() для обхода проверки строки:

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

5. Spring Boot @JdbcTest

Наконец, если мы используем Spring Boot, есть аннотация, которую мы можем использовать для запустите тест с помощью базы данных H2 и bean-компонента JdbcTemplate: @JdbcTest.

Давайте создадим тестовый класс с этой аннотацией:

@JdbcTest
@Sql({"schema.sql", "test-data.sql"})
class EmployeeDAOIntegrationTest {
    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Test
    void whenInjectInMemoryDataSource_thenReturnCorrectEmployeeCount() {
        EmployeeDAO employeeDAO = new EmployeeDAO();
        employeeDAO.setJdbcTemplate(jdbcTemplate);

        assertEquals(4, employeeDAO.getCountOfEmployees());
    }
}

Мы также можем отметить наличие аннотации @Sql, которая позволяет нам указать файлы SQL для запуска перед тестом.

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

В этом руководстве мы показали несколько способов модульного тестирования JdbcTemplate.

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