Опишу свою проблему максимально понятно.
Итак, есть у меня кусок кода A для которого я хочу понаписать юнит тесты (или, интеграционные, не так важно).
class A {
..
@Transactional(propagation=REQUIRES_NEW)
public void oneOfTheMethods() {
...
}
..
}
Написала тест:
@Transactional
public void testAlotOfSutff(){
doSomePreparation();
insertSomeRequiredData();
callProcessB();
..
//at the end, finally we can test processA
callProcessA();
}
Теперь в чем суть проблемы:
мой тест вставляет всякие данные в тестовую базу, вызывает разные подпроцессы, и предполагается что после выполнения тесты все роллбечится, то есть мы не замусориваем тестовую базу ничем плохим, тестируем обе части кода — как которая что-то вставляет, и ту которая работает с этими данными, в общем много плюсов.
Все работало прекрасно. пока мне не понадобилось потестировать в конце как работает метод A.oneOfTheMethods на этих данных — оказалось, что он нифига не работает.
Т.к. он суспендит юниттестовскую транзакцию и создает новую транзакцию (из-за REQUIRED_NEW propagation!)
и соответственно "не видит" данных которые юниттест вставил до этого. СУБД Оракл — поэтому выставить isolation=READ_UNCOMMITTED не удается.
Что делать?
менять propagation только из-за юниттеста я не могу — вопервых он не зря там стоит этот пропагейшн, во=вторых мы не должны менять код который тестируем только потому что не можем нормально написать тест.
Что делать-то?
я согласна даже на чтонибудь типа рефлексией изменять уровень пропагации этого метода ТОЛЬКО для тестирования. но чтоб в продакшне был код без изменений. такое возможно?
Здравствуйте, зиг, Вы писали:
зиг>во-вторых мы не должны менять код который тестируем только потому что не можем нормально написать тест.
Неверно. Частенько рефакторят только ради тестируемости (и в качестве побочного эффекта улучшают архитектуру).
зиг>Что делать-то?
Лично я перед каждым тестом базу пересоздаю (у меня под это дело отдельный экземпляр SQL-сервера крутится с базой в tmpfs — т.е. в RAM). Так что никаких роллбэков-хэков мне не надо, и с этим подходом я могу даже selenium-тесты гонять на задеплоенном приложении, в которое уж точно rollback не подсунешь.
Здравствуйте, зиг, Вы писали:
зиг>Что делать-то? зиг>я согласна даже на чтонибудь типа рефлексией изменять уровень пропагации этого метода ТОЛЬКО для тестирования. но чтоб в продакшне был код без изменений. такое возможно?
Как решение в лоб — грохать базу и пересоздавать заново. Челябинский такой rollback
Здравствуйте, andyag, Вы писали:
A>Здравствуйте, зиг, Вы писали:
зиг>>Что делать-то? зиг>>я согласна даже на чтонибудь типа рефлексией изменять уровень пропагации этого метода ТОЛЬКО для тестирования. но чтоб в продакшне был код без изменений. такое возможно?
A>Как решение в лоб — грохать базу и пересоздавать заново. Челябинский такой rollback
неохота, некрасиво, долго муторно
а что никак с этой транзакцией поисхитряться не получится?
Здравствуйте, зиг, Вы писали:
зиг>Здравствуйте, andyag, Вы писали:
A>>Здравствуйте, зиг, Вы писали:
зиг>>>Что делать-то? зиг>>>я согласна даже на чтонибудь типа рефлексией изменять уровень пропагации этого метода ТОЛЬКО для тестирования. но чтоб в продакшне был код без изменений. такое возможно?
A>>Как решение в лоб — грохать базу и пересоздавать заново. Челябинский такой rollback зиг>неохота, некрасиво, долго муторно зиг>а что никак с этой транзакцией поисхитряться не получится?
Это называется "очистка тестового окружения". Ну не обязательно "грохать", можно просто таблицы почистить. В тестовом классе это можно прописать один раз для всех тестов.
Здравствуйте, Qulac, Вы писали:
Q>Ну не обязательно "грохать", можно просто таблицы почистить.
Ненадёжно/неуниверсально. Вдруг тестируемый код сам таблицы/индексы/а то и вообще хранимки создаёт? А вдруг там хитрые FK-зависимости между таблицами, что запаришься их удалять? Истинно говорю, ничто не сравнится с "drop database" по простоте и чистоте эксперимента! =)
Здравствуйте, зиг, Вы писали:
зиг>я согласна даже на чтонибудь типа рефлексией изменять уровень пропагации этого метода ТОЛЬКО для тестирования. но чтоб в продакшне был код без изменений. такое возможно?
Можно создать тестовый Spring контекст, который тупо игнорирует все Transactional-атрибуты и управлять транзакциями самому, вручную, внутри теста.
Очевидно, надо будет учесть, что такой тест не сможет тестировать всё, т.к. явно может существовать бизнес-логика, которая требует наличие таких атрибуов (unhappy paths, когда что-то кидает исключение и надо что-то закоммитить, а что-то не коммитить). Но для этого можно делать другие тесты, которые воспроизводят контекст более точно...
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Здравствуйте, ., Вы писали:
.>Здравствуйте, зиг, Вы писали:
зиг>>я согласна даже на чтонибудь типа рефлексией изменять уровень пропагации этого метода ТОЛЬКО для тестирования. но чтоб в продакшне был код без изменений. такое возможно? .>Можно создать тестовый Spring контекст, который тупо игнорирует все Transactional-атрибуты и управлять транзакциями самому, вручную, внутри теста. .>Очевидно, надо будет учесть, что такой тест не сможет тестировать всё, т.к. явно может существовать бизнес-логика, которая требует наличие таких атрибуов (unhappy paths, когда что-то кидает исключение и надо что-то закоммитить, а что-то не коммитить). Но для этого можно делать другие тесты, которые воспроизводят контекст более точно...
имеешь ввиду не создавать txManager в текущем тестовом спринговом контексте, соотственно все транзакшнл аннотации будут игнорироваться?
а транзакцию в тесте сздавать самим вручную вначале и роллбечить в конце?помоему это не будет работать
Здравствуйте, dimgel, Вы писали:
D>Лично я перед каждым тестом базу пересоздаю (у меня под это дело отдельный экземпляр SQL-сервера крутится с базой в tmpfs — т.е. в RAM). Так что никаких роллбэков-хэков мне не надо, и с этим подходом я могу даже selenium-тесты гонять на задеплоенном приложении, в которое уж точно rollback не подсунешь.
Про рамдиск решение суперское. Если не секрет, сколько у вас тестов, за сколько проходят на рамдиске, за сколько на обычном HDD?
Здравствуйте, зиг, Вы писали:
зиг>>>я согласна даже на чтонибудь типа рефлексией изменять уровень пропагации этого метода ТОЛЬКО для тестирования. но чтоб в продакшне был код без изменений. такое возможно? .>>Можно создать тестовый Spring контекст, который тупо игнорирует все Transactional-атрибуты и управлять транзакциями самому, вручную, внутри теста. .>>Очевидно, надо будет учесть, что такой тест не сможет тестировать всё, т.к. явно может существовать бизнес-логика, которая требует наличие таких атрибуов (unhappy paths, когда что-то кидает исключение и надо что-то закоммитить, а что-то не коммитить). Но для этого можно делать другие тесты, которые воспроизводят контекст более точно...
зиг>имеешь ввиду не создавать txManager в текущем тестовом спринговом контексте, соотственно все транзакшнл аннотации будут игнорироваться? зиг>а транзакцию в тесте сздавать самим вручную вначале и роллбечить в конце?помоему это не будет работать
Spring контекст где-то содержит код (тег <tx:annotation-driven>), который проверяет наличие @Transactional на бинах контекста и создаёт прокси-объекты, которые шлют соответствующие команды txManager-у.
Вот это тебе надо выбросить из тестового конфига и послать команду txManager-у самому. В setUp методе на начало транзации (getTransaction) и tearDown на откат (rollback).
What could possibly go wrong.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Здравствуйте, andyag, Вы писали:
D>>Лично я перед каждым тестом базу пересоздаю (у меня под это дело отдельный экземпляр SQL-сервера крутится с базой в tmpfs — т.е. в RAM). Так что никаких роллбэков-хэков мне не надо, и с этим подходом я могу даже selenium-тесты гонять на задеплоенном приложении, в которое уж точно rollback не подсунешь.
A>Про рамдиск решение суперское. Если не секрет, сколько у вас тестов, за сколько проходят на рамдиске, за сколько на обычном HDD?
А насколько оно актуально в наступившей эпохе SSD?
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Здравствуйте, ., Вы писали:
зиг>>имеешь ввиду не создавать txManager в текущем тестовом спринговом контексте, соотственно все транзакшнл аннотации будут игнорироваться? зиг>>а транзакцию в тесте сздавать самим вручную вначале и роллбечить в конце?помоему это не будет работать .>Spring контекст где-то содержит код (тег <tx:annotation-driven>), который проверяет наличие @Transactional на бинах контекста и создаёт прокси-объекты, которые шлют соответствующие команды txManager-у. .>Вот это тебе надо выбросить из тестового конфига и послать команду txManager-у самому. В setUp методе на начало транзации (getTransaction) и tearDown на откат (rollback).
супер, все сработало, странно чо это я сама не догадалась!!