![]() |
Модераторы: LSD |
![]() ![]() ![]() |
|
carper |
|
|||
Бывалый ![]() Профиль Группа: Участник Сообщений: 227 Регистрация: 2.3.2005 Репутация: нет Всего: 8 |
Да, я знаю, что JUNIT в основном служит для модульного тестирования, а не интеграционного или функционального.
И, да, я знаю (и использую) про TestNG. Да, я знаю, что взаимозависимость модульных тестов есть зло. Но, хотелось бы услышать, как правильно решить простейшую проблему средствами JUNIT - пусть есть класс с 2-мя методами: addRecord(...) и delRecord(String idrecord); Хотелось бы протестировать оба метода на реальном экземпляре базы данных. Что пока приходит на ум: 1. Тестировать оба метода в ОДНОМ тестовом: addRecord(...); .... delRecord(String idrecord); Не здорово хотя бы с точки зрения названия тестового метода и явного внесения зависимости тестирования delRecord от того отработает ли addRecord. 2. Попробовать левыми способами реализовать удаление (для очистки таблиц после отработки теста addRecord(...)) или добавление (для последующего тестирования delRecord). Тогда придется ломиться в базу напрямую, что не всегда возможно без написания специальной процедуры в самой базе, т.к., в общем случае, добавление/удаление записи на уровне СУБД совсем не обязательно элементарная процедура (например, необходимо вызвать хранимую процедуру/ы + отработает ряд тригерров и т.п. + последовательность вызова этих самых процедур частично лежит на стороне как раз методов JAVA ..). 3. Наплевать и написать mock базу/таблицы, где снимаются возражения по п.2. Долго и на практике приводит к попыткам просто не тестировать. Да и работа с тестовыми таблицами не всегда позволит отловить ошибки типа чрезмерно долгого отрабатывания запросов. 4. Сделать умное лицо и реализовать способ 1 под флагом функционального теста. ![]() |
|||
|
||||
LSD |
|
|||
![]() Leprechaun Software Developer ![]() ![]() ![]() ![]() Профиль Группа: Модератор Сообщений: 15718 Регистрация: 24.3.2004 Где: Dublin Репутация: 1 Всего: 538 |
Для этих целей существуют DbUnit и SQLUnit. Они приводят базу в требуемое состояние перед запуском теста и возвращают в исходное по окончанию.
-------------------- Disclaimer: this post contains explicit depictions of personal opinion. So, if it sounds sarcastic, don't take it seriously. If it sounds dangerous, do not try this at home or at all. And if it offends you, just don't read it. |
|||
|
||||
carper |
|
|||
Бывалый ![]() Профиль Группа: Участник Сообщений: 227 Регистрация: 2.3.2005 Репутация: нет Всего: 8 |
И как DbUnit и SQLUnit решают данную проблему? Получаем способ N2, ну разве что будем ломиться к базе не напрямую, а сперва запустим DbUnit из которого, опять же каким-то левым (для приложения, разумеется, а не DbUnit) способом, будем ломиться к базе. Почему "левым"?, а потому, что единственный не левый как раз и реализован в методах addRecord и delRecord. Погуглил тут и выяснил, что давно идет дискуссия между сторонниками атомарности unit тестов, которые говорят абсолютно правильные слова и их противниками, которые указывают, что на практике иногда такое решение рождает неповоротливых, долго выполняющихся монстров. Правда доводы последних зачастую сводятся к тому, что они пытаются реализовать интеграционные/функциональные тесты, принимают их почему-то за юнит тесты и возмущаются. ![]() Но иногда ... Вот у меня получается, что, например, метод addRecord должен добавить запись в базу и всё. Казалось бы тестируй себе, зачем тебе delRecord? А хотя бы затем, что тест для addRecord должен за собой подчистить базу, а грамотно можно это сделать либо вызвав в ЭТОМ же методе delRecord, или специально для этого случая написать отдельный, свой delTestRecord, который, опять же, надо бы протестировать. А delRecord? И плевать бы ему на то есть ли вообще addRecord, да вот беда - удалять-то можно только то, что уже есть .... ну и далее по кругу. |
|||
|
||||
LSD |
|
|||
![]() Leprechaun Software Developer ![]() ![]() ![]() ![]() Профиль Группа: Модератор Сообщений: 15718 Регистрация: 24.3.2004 Где: Dublin Репутация: 1 Всего: 538 |
Это не "левый способ", а set up и tead down
![]() Проблема первого способа, в том что: 1. Если возникнет проблема в методе add(), то проблема в del() не будет выявлена. 2. del() завязывается на add(), например если ID записи генерируется при вставке и возвращается add(), то del() становится зависим от того насколько корректно было возвращен ID. -------------------- Disclaimer: this post contains explicit depictions of personal opinion. So, if it sounds sarcastic, don't take it seriously. If it sounds dangerous, do not try this at home or at all. And if it offends you, just don't read it. |
|||
|
||||
carper |
|
|||
Бывалый ![]() Профиль Группа: Участник Сообщений: 227 Регистрация: 2.3.2005 Репутация: нет Всего: 8 |
Увы, даже не это, т.к. могут тестироваться еще много методов, не нуждающихся в таких пост и предусловиях.. Похоже, что серебряной пули тут не найти, надо просто выбирать, что хуже. Наверное, все же был прав автор TestNG, когда ввел возможность явно описывать зависимости между тестами, по сути мой метод 1, просто более элегантно оформленный. Правда меня терзают сильные подозрения, что он имел в виду несколько иное, например, случай, когда тестирование при провале предыдущего теста не имеет смысла, а вовсе не давал инструмент для нарушения атомарности тестов. ![]() |
|||
|
||||
LSD |
|
|||
![]() Leprechaun Software Developer ![]() ![]() ![]() ![]() Профиль Группа: Модератор Сообщений: 15718 Регистрация: 24.3.2004 Где: Dublin Репутация: 1 Всего: 538 |
Юнит тест должен выполнятся в неком синтетическом, контролируемом окружении - моки, или специально подготовленная база. И то что ты выполняешь тесты на реальной базе, это уже нарушении идеологии. Поэтому в данном случае set up и tead down это наименьшее из зол.
Что ты будешь делать если у тебя появится несколько методов del(), по ID, по name и т.д.? Будешь несколько раз вызывать add()? -------------------- Disclaimer: this post contains explicit depictions of personal opinion. So, if it sounds sarcastic, don't take it seriously. If it sounds dangerous, do not try this at home or at all. And if it offends you, just don't read it. |
|||
|
||||
batigoal |
|
|||
![]() Нелетучий Мыш ![]() ![]() ![]() ![]() Профиль Группа: Участник Клуба Сообщений: 6423 Регистрация: 28.12.2004 Где: Санктъ-Петербургъ Репутация: нет Всего: 151 |
LSD, как я понимаю, у carper нет задачи сделать юнит-тест. Он выполняет интеграционный тест средствами JUnit - вполне нормальная задача.
-------------------- "Чтобы правильно задать вопрос, нужно знать большую часть ответа" (Р. Шекли) ЖоржЖЖ |
|||
|
||||
carper |
|
||||
Бывалый ![]() Профиль Группа: Участник Сообщений: 227 Регистрация: 2.3.2005 Репутация: нет Всего: 8 |
Был бы это обычный интеграционный тест, то и вопросов бы не было. Я привел предусловия: хочу протестировать каждый метод отдельно, тем более, что они взаимонезависимы - чтобы что-то добавить не нужен метод для удаления, чтобы что-то удалить не нужен метод для добавления. С другой стороны для этого придется реализовать функционал обоих этих методов каким-либо еще, третьим способом и не хочется тратить на написание теста в 10 раз больше усилий, чем для создания метода. Поэтому хотел бы понять, а нет ли логической ошибки в рассуждениях - в конце-концов тестирование операций типа Create Update Delete (не обязательно именно в базе) очень частая операция.
В первом посте предложил несколько своих кривых вариантов, один из них как раз именно такой - несколько раз вызывать add(). ![]() И как раз хочу понять, а как правильно? Если единственный ответ - да, надо продублировать функционал обоих методов каким-то третьим способом, то вопросов больше нет. Разве что этот третий способ, в свою очередь не застрахован от ошибок! Вот и будет, например, получаться, что в тестовой среде запись будет успешно удаляться только потому, что при ее вставке для теста третьим способом забыл добавить что-то в одну из 15 взаимосвязанных таблиц. А где дублировать этот функционал в тестовой базе, tearUp и прочем, это уже не так важно. |
||||
|
|||||
LSD |
|
|||
![]() Leprechaun Software Developer ![]() ![]() ![]() ![]() Профиль Группа: Модератор Сообщений: 15718 Регистрация: 24.3.2004 Где: Dublin Репутация: 1 Всего: 538 |
Нет никакого дублирования функционала. Подготовка входных параметров, подготовка среды, проверка результата - это все часть теста. Юнит тест вообще ничего не должен знать об устройстве системы, о том, что методы add() выполняют противоположные действия dell().
И кстати, как ты проверяешь, что данные добавить/удалились? Через get() метод? -------------------- Disclaimer: this post contains explicit depictions of personal opinion. So, if it sounds sarcastic, don't take it seriously. If it sounds dangerous, do not try this at home or at all. And if it offends you, just don't read it. |
|||
|
||||
carper |
|
||||
Бывалый ![]() Профиль Группа: Участник Сообщений: 227 Регистрация: 2.3.2005 Репутация: нет Всего: 8 |
Подпишусь под каждым словом. А теперь как же на практике реализовать то, что я написал? Обычный псевдокод. Например:
Пока для обеспечения чистоты эксперимента видится все же создание тестовых таблиц записи в которых будут заполняться при помощи непроверенного кода. Тогда юнит тесты пройдут, а остальное ловим на интеграционных/функциональных тестах. Какой-то неприятный вариант получается, мне так кажется. Нет? |
||||
|
|||||
LSD |
|
|||
![]() Leprechaun Software Developer ![]() ![]() ![]() ![]() Профиль Группа: Модератор Сообщений: 15718 Регистрация: 24.3.2004 Где: Dublin Репутация: 1 Всего: 538 |
Тестирование сложной бизнес логики, требует достаточно сложных проверок. Ты еще не учел, что тебе после того как вызовешь add()/dell() надо будет проверить эти 15 таблиц, на предмет добавились ли данные. -------------------- Disclaimer: this post contains explicit depictions of personal opinion. So, if it sounds sarcastic, don't take it seriously. If it sounds dangerous, do not try this at home or at all. And if it offends you, just don't read it. |
|||
|
||||
carper |
|
|||
Бывалый ![]() Профиль Группа: Участник Сообщений: 227 Регистрация: 2.3.2005 Репутация: нет Всего: 8 |
Хм...
Интересно, а как проводить даже интеграционный тест, если имеется бин "A" со временем жизни singleton, использующий бин "B" со временем жизни session. Причем интересует правильно ли инициализируется бин "B" в "A". Вопрос не потому, что хочется тестировать внутренний функционал Spring, а потому, что бин "A" предполагается использовать в отдельном потоке и тут возможны нюансы. Т.е. бину можно устроить в тесте нечто вроде: @Autowired BeanA beanA_1; @Autowired BeanA beanA_2; Но для полноценного теста надо проверить работу с разными сессиями, а при вышеназванном варианте, получается, что beanA_1.getBeanB() == beanA_2.getBeanB(); Можно получать бины А из фабрики - сути это не меняет, т.е. для теста не годится. :( |
|||
|
||||
LSD |
|
|||
![]() Leprechaun Software Developer ![]() ![]() ![]() ![]() Профиль Группа: Модератор Сообщений: 15718 Регистрация: 24.3.2004 Где: Dublin Репутация: 1 Всего: 538 |
А откуда бин А берет бин В? -------------------- Disclaimer: this post contains explicit depictions of personal opinion. So, if it sounds sarcastic, don't take it seriously. If it sounds dangerous, do not try this at home or at all. And if it offends you, just don't read it. |
|||
|
||||
carper |
|
|||
Бывалый ![]() Профиль Группа: Участник Сообщений: 227 Регистрация: 2.3.2005 Репутация: нет Всего: 8 |
|
|||
|
||||
![]() ![]() ![]() |
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей) | |
0 Пользователей: | |
« Предыдущая тема | Java: Design, Quality, Testing | Следующая тема » |
|
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности Powered by Invision Power Board(R) 1.3 © 2003 IPS, Inc. |