Как откатить транзакцию базы данных при тестировании служб с помощью Spring в JUnit?
У меня нет проблем с тестированием моих DAO и служб, но когда я тестирую INSERT
или UPDATE
, я хочу отменить транзакцию и не повлиять на мою базу данных.
Я использую @Transactional
внутри своих служб для управления транзакциями. Я хочу знать, возможно ли узнать, будет ли транзакция в порядке, но откатить ее, чтобы предотвратить изменение базы данных?
Это мой тест:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:/META-INF/spring.cfg.xml")
@TransactionConfiguration(defaultRollback=true)
public class MyServiceTest extends AbstractJUnit38SpringContextTests {
@Autowired
private MyService myService;
@BeforeClass
public static void setUpClass() throws Exception {
}
@AfterClass
public static void tearDownClass() throws Exception {
}
@Test
public void testInsert(){
long id = myService.addPerson( "JUNIT" );
assertNotNull( id );
if( id < 1 ){
fail();
}
}
}
Проблема в том, что этот тест завершится неудачно, потому что транзакция была отменена, но вставка в порядке!
Если я удалю @TransactionConfiguration(defaultRollback=true)
, тогда тестовый проход, но новая запись будет вставлена в базу данных.
@Test
@Transactional
@Rollback(true)
public void testInsert(){
long id = myService.addPerson( "JUNIT" );
assertNotNull(id);
if( id < 1 ){
fail();
}
}
Теперь можно выполнить пробный проход правильно, но откат игнорируется, и запись вставляется в базу данных.
Я отметил, что метод addPerson()
внутри myService с @Transactional
, очевидно.
Почему игнорирование отката?
Ответы
Ответ 1
Вам необходимо расширить границы транзакций до границ вашего тестового метода. Вы можете сделать это, аннотируя свой тестовый метод (или весь тестовый класс) как @Transactional
:
@Test
@Transactional
public void testInsert(){
long id=myService.addPerson("JUNIT");
assertNotNull(id);
if(id<1){
fail();
}
}
Вы также можете использовать этот подход для обеспечения правильной записи данных перед откатом:
@Autowired SessionFactory sf;
@Test
@Transactional
public void testInsert(){
myService.addPerson("JUNIT");
sf.getCurrentSession().flush();
sf.getCurrentSession().doWork( ... check database state ... );
}
Ответ 2
проверить
http://static.springsource.org/spring/docs/2.5.x/reference/testing.html
Раздел 8.3.4, в частности
Spring имеет несколько классов для тестирования, которые будут обертывать каждый тест в транзакции, поэтому БД не изменяется. Вы также можете изменить эту функциональность, если хотите.
Изменить - в зависимости от вашей информации, вы можете посмотреть
AbstractTransactionalJUnit38SpringContextTests в
http://static.springsource.org/spring/docs/2.5.x/api/org/springframework/test/context/junit38/AbstractTransactionalJUnit38SpringContextTests.html
Ответ 3
Используйте следующую аннотацию перед классом:
@TransactionConfiguration(transactionManager = "txManager",defaultRollback = true)
@Transactional
здесь txManager
- это диспетчер транзакций приложения.
Здесь txManager
- это экземпляр или bean id менеджера транзакций из application context
.
<!-- Transaction Manager -->
<bean id="txManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<tx:annotation-driven transaction-manager="txManager" />
Добавьте свой код внутри метода setUp()
, это будет выполняться в начале теста, а последний завершающий код должен быть помещен в метод teatDown()
, который будет выполнен последним. или вы можете использовать вместо него @Before
и @After
аннотацию.
Ответ 4
Если ваш
myService.addPerson("JUNIT");
метод аннотируется как @Transactional, вы будете получать какой-то другой вид или ошибки, пытающиеся исправить это. Так что вам лучше просто проверить методы DAO.