Как я могу использовать модульные тесты и TDD для тестирования приложения, которое в основном опирается на операции CRUD базы данных?

22

На работе один из моих проектов в основном связан с передачей данных от внешнего клиента и сохранением их в базе данных. Это корпоративное Java-приложение, использующее JPA, и большая часть нашей логики вращается вокруг операций CRUD.

Большинство наших ошибок так или иначе связаны с JPA.

  • Пример 1. Если вы дважды нажмете кнопку сохранения, JPA может попытаться вставить один и тот же объект в базу данных еще раз, что приведет к нарушению первичного ключа.
  • Пример 2. Вы извлекаете объект из базы данных, редактируете его и пытаетесь обновить его данные. JPA может попытаться создать новый экземпляр вместо обновления старого.

Часто решение нуждается в добавлении / удалении / изменении аннотации JPA. В других случаях это связано с изменением логики DAO.

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

Модульные тесты кажутся неподходящими, потому что я могу обнаружить эти проблемы только во время выполнения, и мне нужно развернуть их на сервере приложений, чтобы воспроизвести проблемы. Обычно необходимо задействовать базу данных, которая, как я считаю, выходит за рамки определения модульного теста: это интеграционные тесты.

TDD кажется неподходящим, потому что цикл обратной связи deploy + test настолько медленный, что делает меня очень непродуктивным. Цикл обратной связи deploy + test занимает более 3 минут, и это только в том случае, если я запускаю тесты специально для кода, который пишу. Для запуска всех интеграционных тестов требуется более 30 минут.

За пределами этой формы есть код, и я всегда тестирую его по мере возможности. Но большинство наших ошибок и самые большие временные погрешности всегда связаны с JPA или базой данных.


Есть еще один похожий вопрос , но если бы я следовал совету, я бы обернул самую нестабильную часть своего кода (JPA) и протестировал все, кроме него. В контексте моего вопроса я был бы в такой же плохой ситуации. Какой следующий шаг после упаковки JPA? ИМО этот вопрос (возможно) является шагом к ответу на мой вопрос, но не является ответом на него.

Даниэль Каплан
источник
4
То, что вы делаете, - это, по сути, интеграционное тестирование, поскольку вы должны настроить базу данных для фактического тестирования. Я могу себе представить, что один модуль будет полагаться на другие, поэтому он будет еще больше похож на интеграционный тест. Я бы изменил вопрос, который вам нужен, на то, как применять подходы TDD к вашему приложению.
InformedA
@randomA правильно, я отредактировал свой вопрос, чтобы прямо сказать это. Я не понимаю, почему вы рекомендуете изменить вопрос. Можете ли вы уточнить? Я хочу оставить там часть модульного теста, потому что я скорее буду писать модульные тесты, чем интеграционные тесты (хотя я в курсе unit testing != TDD)
Даниэль Каплан
ничего особенного, просто положите туда TDD. Если у вас там есть юнит-тест, то многие люди подумают, что вы ничего не понимаете и т. Д. Не очень хорошо для вас ...
InformedA

Ответы:

7

Одним из вариантов является использование базы данных тестирования в памяти, такой как H2 ; как правило, примерно в 10 раз быстрее, чем стандартная база данных, использующая диск, и с меньшим временем запуска / демонтажа.

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

Но если вы можете сделать 10 прогонов с H2 для каждого с полной системой, это может окупиться.

Сору
источник
Это хорошая мысль, но мне все равно придется развернуться на сервере приложений, AFAIK. Это много 3+ минут. Тем не менее, это определенно стоит сделать. Но по-прежнему сложно представить выполнение тестов так часто, как я бы запускал модульные тесты, и поэтому разработка с использованием TDD кажется неэффективной.
Даниэль Каплан
1
Я думаю, что обычно есть способы обойти это требование (например, docs.oracle.com/middleware/1212/toplink/TLADG/testingjpa.htm ). Довольно высокий шанс быть более трудным, чем оправдано; Другой вариант - получить несколько лучших серверов для тестирования и запустить их параллельно.
сору
1
@tieTYT Мое собственное доказательство концепции модульного тестирования hsqldb веб-приложения crud на github: TestingWithHsqldb - модульные тесты не требуют развертывания приложения.
3

Базы данных могут быть очень просты для модульного тестирования - вам нужны хранимые процедуры и транзакции.

Это то, что Microsoft говорит о модульном тестировании базы данных . Вы также можете запускать модульные тесты для базы данных, писать свои тесты на Java или C #, устанавливая соединение с БД, начинать транзакцию, записывать любые данные, которые вы хотите использовать для теста, в БД, запускать тесты и затем выполнять откат. Никаких повреждений БД, если вы использовали тот, на котором вы также развернули и получили полностью изолированные тесты.

Надеюсь, что это может дать вам некоторое представление о том, как это сделать в вашей структуре.

gbjbaanb
источник
Как я уже сказал: «Большинство наших ошибок так или иначе связаны с JPA», я думаю, что совет статьи будет пропускать все из них. Кроме того, если вы считаете, что эти тесты Java / C # по-прежнему являются модульными тестами, у нас есть очень разные определения. Я думаю, что это хороший совет в целом, но все же кажется, что для развертывания и запуска пакета потребуется куча времени, и поэтому он не способствует TDD. Вы не согласны?
Даниэль Каплан
Мы использовали для запуска модульных тестов БД для нашего SQL, но тогда все они были в sprocs. В то время как вы можете блок каталог теста SQL из других процедур SQL, наша модульное тестирование рамки была MSTest поэтому имел смысл запускать их оттуда (эй, мы получили зеленые галочки в сервере сборки , который был наиболее важным фактором). Если у вас есть постоянно работающая БД (и мы все равно делали это для внутреннего тестирования), то легко загрузить весь код SQL и запустить все модульные тесты на сервере сборки. Иногда вы просто должны быть прагматичными в этих вещах.
gbjbaanb
Я не думаю, что вы ответили на мое первое предложение.
Даниэль Каплан
ну, тогда просто используйте jpa-модуль. Я не могу ответить, как работает ваш код JPA (или нет), просто попробуйте дать вам несколько идей о том, как протестировать этот sql в БД.
gbjbaanb
3

Другие люди ответили: «Смейся!» - но какой смысл в том, чтобы макетировать слой вашей БД, если вам действительно нужно проверить, как он взаимодействует с вашим кодом?

То, что вы ищете, это интеграционные тесты и / или автоматизированные тесты пользовательского интерфейса. Вы упомянули, что проблема возникает, когда:

*If you click the save button twice*

Единственный способ проверить это - написать автоматический тест пользовательского интерфейса, дважды нажав на кнопку. Может быть, проверить Селен.

Возможно, вам также понадобится БД для модульного тестирования, и ваши тесты указывают на это. Боль поддерживать, но добро пожаловать в TDD в реальном мире.

Rocklan
источник
это больше похоже на разглагольствование, чем на ответ
комнат
Я ответил на вопрос три раза - интеграционные тесты, тесты GUI и / или БД модульного тестирования. Да, это немного напыщенная речь, сейчас я отредактирую это до некоторой степени здравомыслия.
Rocklan
1
«Единственный способ проверить это - написать автоматизированный тест пользовательского интерфейса, дважды щелкнув по кнопке. Может быть, посмотрите Selenium». В подобных ситуациях бэкэнд лучше предотвращать, иначе пользовательский интерфейс будет иметь прямой доступ к базе данных.
Даниэль Каплан
0

В примере, который вы приводите в своем вопросе, вы не можете провести модульное тестирование / TDD, если дважды нажмите кнопку, чтобы очень легко вызвать ошибку. Но то, что вы можете выполнить модульное тестирование, это то, что в коде, который вызывается при нажатии кнопки, если вы получаете исключение из уровня персистентности, вы обрабатываете его соответствующим образом (либо путем макетирования уровня персистентности, либо с использованием базы данных в памяти как было предложено в других ответах) - либо перебрасыванием, либо отображением ошибки или чем-то еще.

Вы правы в том, что TDD может начать выходить из строя, когда вам нужно выполнить тесты, которые не подходят для модульного теста (например, интеграционные / системные тесты) - это сформировало довольно много дискуссий в недавнем "Is TDD" Мертв?" дебаты между Кентом Беком, Мартином Фаулером и Дэвидом Хейнемайером Ханссоном: http://martinfowler.com/articles/is-tdd-dead/

Крис Купер
источник