Откат транзакции после @Test

86

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

Я запускаю тесты JUnit с помощью spring -test, мой код выглядит так

@RunWith(SpringJUnit4ClassRunner.class)  
@ContextConfiguration(locations = {})
public class StudentSystemTest {

    @Autowired
    private StudentSystem studentSystem;

    @Before
    public void initTest() {
    // set up the database, create basic structure for testing
    }

    @Test
    public void test1() {
    }    
    ...  
}

Моя проблема в том, что я хочу, чтобы мои тесты НЕ влияли на другие тесты. Поэтому я бы хотел создать что-то вроде отката для каждого теста. Я много искал это, но пока ничего не нашел. Я использую для этого Hibernate и MySql

Ян Ворчак
источник
Что значит откат? Очистка базы данных?
Gaurav
5
установив его в то же состояние, в котором он был после выполненияinitTest
Ян Ворчак

Ответы:

129

Просто добавьте @Transactionalаннотацию поверх теста:

@RunWith(SpringJUnit4ClassRunner.class)  
@ContextConfiguration(locations = {"testContext.xml"})
@Transactional
public class StudentSystemTest {

По умолчанию Spring начнет новую транзакцию, связанную с вашим тестовым методом и @Before/ @Afterобратными вызовами, откатываясь в конце. Он работает по умолчанию, достаточно иметь в контексте какой-нибудь менеджер транзакций.

От: 10.3.5.4 Управление транзакциями (выделено жирным шрифтом):

В структуре TestContext транзакциями управляет TransactionalTestExecutionListener. Обратите внимание, что TransactionalTestExecutionListenerэто настроено по умолчанию , даже если вы явно не объявляете его @TestExecutionListenersв своем тестовом классе. Однако, чтобы включить поддержку транзакций, вы должны предоставить PlatformTransactionManagerкомпонент в контексте приложения, загруженный @ContextConfigurationсемантикой. Кроме того, вы должны объявить @Transactionalдля своих тестов либо на уровне класса, либо на уровне метода .

Томаш Нуркевич
источник
2
ну, я пробовал это раньше, и это все еще не работает, может быть ... может проблема в том, что я не определил PlatformTransactionManager, как я могу это сделать?
Ян Ворчак,
@javo: как вы меняете базу данных? Если вы используете Jpa / Hibernate / JdbcTemplate / ... они должны быть PlatformTransactionManager. Иначе как Spring узнает о ваших транзакциях и базе данных?
Tomasz Nurkiewicz
1
Ссылка в этом ответе больше не верна; см . ответ user2418306 ниже, чтобы найти правильную ссылку и дополнительный контекст из документации Spring.
DaveyDaveDave
1
Я не работаю, каждый метод требует отдельной транзакции, не могу сделать это для всего класса
Камил Неканович
Тестирую вставку в стол. С @Transactional команда вставки даже не выполняется для базы данных (я вижу это, потому что консоль имеет hibernate show-sql true). Во многих случаях работает нормально. Но один из моих тестов проверяет, что база данных выдает исключение DataAccessException из-за ограничения базы данных. В этом случае тест не проходит, потому что откат происходит «в памяти», а база данных никогда не вызывается. Решение: используйте @Transactionalна @Testуровне метода, а не на уровне класса.
Пауло Мерсон
17

В сторону : попытка изменить ответ Томаша Нуркевича была отклонена:

Это редактирование не делает сообщение даже немного более легким для чтения, более легким для поиска, более точным или доступным. Изменения либо совершенно излишни, либо сильно ухудшают читабельность.


Правильная и постоянная ссылка на соответствующий раздел документации по интеграционному тестированию.

Чтобы включить поддержку транзакций, вы должны сконфигурировать PlatformTransactionManagerbean-компонент, ApplicationContextкоторый загружается через @ContextConfigurationсемантику.

@ Конфигурация
@PropertySource ("application.properties")
public class Persistence {
    @Autowired
    Environment env;

    @Bean
    DataSource dataSource () {
        вернуть новый DriverManagerDataSource (
                env.getProperty ("datasource.url"),
                env.getProperty ("datasource.user"),
                env.getProperty ("datasource.password")
        );
    }

    @Bean
    PlatformTransactionManager transactionManager () {
        вернуть новый DataSourceTransactionManager (dataSource ());
    }
}

Кроме того, вы должны объявить @Transactionalаннотацию Spring на уровне класса или метода для ваших тестов.

@RunWith (SpringJUnit4ClassRunner.class)
@ContextConfiguration (классы = {Persistence.class, SomeRepository.class})
@Transactional
открытый класс SomeRepositoryTest {...}

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

user2418306
источник
12

Ответы, в которых упоминается добавление @Transactional, верны, но для простоты вы можете просто иметь свой тестовый класс extends AbstractTransactionalJUnit4SpringContextTests.

Мэтт Би
источник
1
добавление аннотации «@Transactional» на уровне класса не работает, добавление аннотации «@Transactional» отдельно для каждой функции работает, а также расширяет AbstractTransactionalJUnit4SpringContextTests.
Камил Неканович
5

Я знаю, я слишком поздно, чтобы публиковать ответ, но надеюсь, что это может кому-то помочь. Кроме того, я только что решил эту проблему с моими тестами. Вот что у меня было в тесте:

Мой тестовый класс

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "path-to-context" })
@Transactional
public class MyIntegrationTest 

Контекст xml

<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
   <property name="driverClassName" value="${jdbc.driverClassName}" />
   <property name="url" value="${jdbc.url}" />
   <property name="username" value="${jdbc.username}" />
   <property name="password" value="${jdbc.password}" />
</bean>

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

Проблема была решена, когда я добавил следующее свойство в BasicDataSource

<property name="defaultAutoCommit" value="false" />

Надеюсь, поможет.

Атул Кумбхар
источник
Итак, вы фиксируете свои утверждения вручную? Вы уверены, что ваши данные вообще были записаны в вашу базу данных?
franta kocourek
Для всех, кто пытается понять Spring Transactions, убедитесь, что ваш источник данных НЕ настроен на автоматическую фиксацию, иначе вы сведете себя с ума, пытаясь понять, почему @Transactional, похоже, ничего не делает.
Джо Эрнст
2

Вам нужно запустить свой тест с контекстом Spring и менеджером транзакций, например,

@RunWith(SpringJUnit4ClassRunner.class)  
@ContextConfiguration(locations = {"/your-applicationContext.xml"})
@TransactionConfiguration(transactionManager="txMgr")
public class StudentSystemTest {

     @Test
     public void testTransactionalService() {
         // test transactional service
     }

     @Test
     @Transactional
     public void testNonTransactionalService() {
         // test non-transactional service
     }
}

См. Главу 3.5.8. Transaction Management справочника Spring для получения дополнительной информации.

Йохан Сьёберг
источник
0

В дополнение к добавлению @Transactionalпо @Testметоду, вам также необходимо добавить@Rollback(false)

user2615724
источник
-5

Вы можете отключить откат:

@TransactionConfiguration(defaultRollback = false)

Пример:

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = Application.class)
@Transactional
@TransactionConfiguration(defaultRollback = false)
public class Test {
    @PersistenceContext
    private EntityManager em;

    @org.junit.Test
    public void menge() {
        PersistentObject object = new PersistentObject();
        em.persist(object);
        em.flush();
    }
}
DwD
источник
6
Это прямо противоположно тому, о чем просит OP
Адам Михалик,
Это должен был быть комментарий к принятому ответу.
DerMike