Интеграционное тестирование в Spring: TestContext Framework, включение и отключение транзакций

Аннотирование метода теста с помощью @Transactional приводит к тому, что тест запускается в транзакции, которая по умолчанию автоматически откатывается после завершения теста. Если тестовый класс аннотируется @Transactional, каждый тестовый метод в этой иерархии классов выполняется внутри транзакции. Методы тестирования, не помеченные @Transactional (на уровне класса или метода), не выполняются в транзакции. Обратите внимание, что @Transactional не поддерживается в методах жизненного цикла теста - например, методы, аннотированные JUnit Jupiter @BeforeAll, @BeforeEach и т.д. Кроме того, тесты, аннотированные @Transactional, но имеющие атрибут propagation, установленный на NOT_SUPPORTED, не выполняются в пределах транзакции.

Поддержка атрибута @Transactional

Атрибут Поддерживается для транзакций, управляемых тестами
value и transactionManager да
propagation только Propagation.NOT_SUPPORTED и Propagation.NEVER поддерживаются
isolation нет
timeout нет
readOnly нет
rollbackFor и rollbackForClassName нет: используйте вместо этого TestTransaction.flagForRollback()
noRollbackFor и noRollbackForClassName нет: используйте вместо этого TestTransaction.flagForCommit()

Методы жизненного цикла на уровне методов - например, методы, аннотированные JUnit Jupiter @BeforeEach или @AfterEach - выполняются в рамках транзакции, управляемой тестом. С другой стороны, методы жизненного цикла уровня набора и уровня класса - например, методы, аннотированные JUnit Jupiter @BeforeAll или @AfterAll, и методы, аннотированные TestNG @BeforeSuite, @AfterSuite, @BeforeClass или @AfterClass - не выполняются внутри транзакции, управляемой тестами.

Если вам нужно запустить код в методе жизненного цикла уровня набора или уровня класса внутри транзакции, вы можете добавить соответствующий PlatformTransactionManager в свой тестовый класс, а затем использовать его с TransactionTemplate для программного управления транзакциями.

Обратите внимание, что AbstractTransactionalJUnit4SpringContextTests и AbstractTransactionalTestNGSpringContextTests предварительно настроены для поддержки транзакций на уровне класса.

В следующем примере демонстрируется общий сценарий написания интеграционного теста для UserRepository на основе Hibernate:

@SpringJUnitConfig(TestConfig.class)
@Transactional
class HibernateUserRepositoryTests {

    @Autowired
    HibernateUserRepository repository;

    @Autowired
    SessionFactory sessionFactory;

    JdbcTemplate jdbcTemplate;

    @Autowired
    void setDataSource(DataSource dataSource) {
        this.jdbcTemplate = new JdbcTemplate(dataSource);
    }

    @Test
    void createUser() {
        // отслеживаем начальное состояние 
        // в тестовой базе данных:
        final int count = countRowsInTable("user");

        User user = new User(...);
        repository.save(user);

        // Ручной flush необходим, 
        // чтобы избежать ложного срабатывания теста
        sessionFactory.getCurrentSession().flush();
        assertNumUsers(count + 1);
    }

    private int countRowsInTable(String tableName) {
        return JdbcTestUtils.countRowsInTable(this.jdbcTemplate, tableName);
    }

    private void assertNumUsers(int expected) {
        assertEquals("Number of rows in the [user] table.", expected, countRowsInTable("user"));
    }
}

Нет необходимости очищать базу данных после запуска метода createUser(), поскольку любые изменения, внесенные в базу данных, автоматически откатываются TransactionalTestExecutionListener.

Поведение отката и фиксации транзакции

По умолчанию тестовые транзакции автоматически откатываются после завершения теста; однако поведение фиксации и отката транзакции можно настроить декларативно с помощью аннотаций @Commit и @Rollback.


Читайте также:


Комментарии

Популярные сообщения из этого блога

Методы класса Object в Java

Как получить текущий timestamp в Java

Основные опции JVM для повышения производительности и отладки