Spring Boot test с @Sql блокирует таблицы и репозитории, а сервисы не читают - PullRequest
0 голосов
/ 07 октября 2019

Я использую SQL Server 2016 в качестве базы данных для производства и разработки (поэтому нет h2) с бэкэндом Spring Boot 2 и внешним интерфейсом Angular 7. Если я запускаю приложение нормально, все работает как положено. Для интеграционного тестирования (JUnit5) я бы хотел использовать Selenium, поэтому приложение должно фактически работать на порте и быть доступным через браузер. Мне также нужно подготовить некоторые данные перед каждым тестом (так как я не могу использовать производственные данные). Для достижения обоих целей я планировал использовать аннотацию @Sql пружины , которая позволяет мне выполнять любой файл .sql перед тестами (в моем случае я вставляю данные, которыми хотел бы манипулировать). Спринг откатывает все обратно после теста, поэтому должно хорошо работать. Однако при вставке данных запущенная тестом транзакция блокирует таблицы базы данных, а другие службы / репозитории, используемые приложением (например, для запроса данных), блокируются.

Пример. : Я вставляю сотрудника в файл, на который я ссылаюсь в аннотации @Sql, затем Selenium запускает браузер и перехожу к списку сотрудников. На этом этапе список сотрудников не будет работать (который обслуживается с использованием конечной точки REST и EmployeeRepository), поскольку он заблокирован транзакцией теста.

Я также могу подтвердить эту блокировку вручную, хотяпри выполнении тестов я не могу выполнить запросы с помощью SQL Server Management Studio (ожидание и завершение после завершения тестов).

Могу ли я использовать @Sql или любой другой инструмент для подготовки данных для моих тестов и позволить приложению работатьнормально, а также возможность откатить изменения после тестов?

Тестовый класс:

@SpringBootTest(classes = ...App.class)
@ActiveProfiles(profiles = "test")
@ExtendWith(SpringExtension.class)
@Transactional
public class SeleniumExampleIT {
    @LocalServerPort
    protected int port;

    protected WebDriver driver;
    protected NgWebDriver ngWebDriver;

    @BeforeAll
    public static void setupClass() {
        WebDriverManager.chromedriver().setup();
    }

    @BeforeEach
    public void setupTest() {
        var chromeDriver = new ChromeDriver();
        driver = chromeDriver;
        ngWebDriver = new NgWebDriver(chromeDriver);
        ngWebDriver.waitForAngularRequestsToFinish();
    }

    @AfterEach
    public void teardown() {
        if (driver != null) {
            driver.quit();
        }
    }

    @Test
    @Sql({"classpath:sql/test.sql"})
    void listEmployeesTest() {
        //...starting selenium
        // navigating, waiting ..etc
    }
}

EmployeeRepository:

@Repository
public interface EmployeeRepository extends JpaRepository<Employee, Long> {
    @Query("SELECT DISTINCT e FROM Employee e " +
        "LEFT JOIN FETCH e.settings")
    List<Employee> findAll();
    @Query("SELECT DISTINCT e FROM Employee e " +
        "LEFT JOIN FETCH e.settings " +
        "WHERE e.id = :id")
    Optional<Employee> findById(@Param("id") Long id);
}

Редактировать .:Аннотирование JpaRepository методов с помощью @Transactional не решило проблему (как уже упоминалось здесь ).

Edit2 .: Согласно Документам Spring

Если ваш тест равен @Transactional, он по умолчанию откатывает транзакцию в конце каждого метода тестирования. Однако, поскольку использование этого соглашения с RANDOM_PORT или DEFINED_PORT неявно обеспечивает реальную среду сервлета, HTTP-клиент и сервер работают в отдельных потоках и, следовательно, в отдельных транзакциях. Любая транзакция, инициированная на сервере, в этом случае не откатывается.

1 Ответ

1 голос
/ 14 октября 2019

Единственное решение, которое я смог найти, это НЕ использовать @Transactional тесты. Я создал отдельную базу данных, которая используется только для тестовых прогонов. Я очищаю базу данных ДО каждого теста, используя такой скрипт:

-- CHECK IF DATABASE HAS TESTING ALLOWED
IF (OBJECT_ID('dbo.tests_allowed') IS NULL)
BEGIN
    RAISERROR('Testing is NOT allowed in the selected database! Please, check carefully, before removing all data! If you want to allow testing, create table dbo.`tests_allowed`.', 16, 1)
END;

-- DISABLE CHECKS
ALTER TABLE dbo.<table-names> NOCHECK CONSTRAINT ALL;

-- TRUNCATE TABLES
DELETE FROM dbo.<table-names>;

-- ENABLE CHECKS
ALTER TABLE dbo.<table-names> WITH CHECK CHECK CONSTRAINT ALL;

После запуска усечения я подготавливаю базу данных с помощью скрипта вставки данных, выполняя его так:

@Test
@DisplayName("Checking settings....")
@Sql(value = {"classpath:sql/truncate-tables.sql", "classpath:sql/example-dataset-1.sql"},
    config = @SqlConfig(transactionMode = SqlConfig.TransactionMode.ISOLATED))
public void check...Test() {}

Используя режим ISOLATED, файлы Sql будут зафиксированы сразу, и я смогу прочитать данные во время тестов.

Таким образом .:

  • База данных всегда очищается передтесты, и мне не нужно беспокоиться о грязных данных во время теста
  • Если тест не пройден, я могу проверить базу данных в том состоянии, в котором она была неудачной
  • Проверка перед тем, как скрипт truncate предотвращаетзапуск теста в производственной среде или среде разработки (я вручную добавляю таблицу tests_allowed в базу данных теста).
  • Не нужно беспокоиться о том, что Hibernate не фиксирует данные или транзакции блокируют / блокируют блокировку
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...