Есть ответы на вопрос о том, как тестовые классы подключаются к базе данных, например, «Должны ли сервисные тестовые классы подключаться ...» и «Модульное тестирование - приложение, связанное с базой данных» .
Итак, вкратце, давайте предположим, что у вас есть класс A, который должен подключаться к базе данных. Вместо того, чтобы позволить A фактически соединяться, вы предоставляете A интерфейс, который A может использовать для подключения. Для тестирования вы реализуете этот интерфейс с некоторыми вещами - без подключения, конечно. Если класс B создает экземпляр A, он должен передать «реальное» соединение с базой данных A. Но это означает, что B открывает соединение с базой данных. Это означает, что для проверки B вы вводите соединение в B. Но B создается в классе C и так далее.
Итак, в какой момент я должен сказать «здесь я получаю данные из базы данных, и я не буду писать модульный тест для этого куска кода»?
Другими словами: где-то в коде в каком-то классе я должен позвонить sqlDB.connect()
или что-то подобное. Как мне проверить этот класс?
И то же самое с кодом, который имеет дело с GUI или файловой системой?
Я хочу сделать юнит-тест. Любой другой вид теста не связан с моим вопросом. Я знаю, что я буду тестировать только один класс с ним (я так согласен с вами, Килиан). Теперь какой-то класс должен подключиться к БД. Если я хочу протестировать этот класс и спросить «Как мне это сделать», многие скажут: «Используйте инъекцию зависимости!» Но это только переносит проблему в другой класс, не так ли? Поэтому я спрашиваю, как я могу проверить класс, который действительно устанавливает связь?
Бонусный вопрос: некоторые ответы здесь сводятся к «Использовать фиктивные объекты!» Что это значит? Я издеваюсь над классами, от которых зависит тестируемый класс. Должен ли я сейчас издеваться над тестируемым классом и проверять макет (что близко к идее использования методов шаблона, см. Ниже)?
источник
Ответы:
Суть модульного теста состоит в том, чтобы протестировать один класс (фактически, он обычно должен тестировать один метод ).
Это означает, что когда вы тестируете класс
A
, вы вставляете в него тестовую базу данных - что-то написанное самим собой или молниеносную базу данных в памяти, что бы ни выполнялось.Однако, если вы тестируете класс
B
, который является клиентомA
, то обычно вы имитируете весьA
объект чем-то другим, предположительно чем-то, что выполняет свою работу примитивным, предварительно запрограммированным способом - без использования реальногоA
объекта и, конечно, без использования данных. base (если только неA
передать все соединение с базой данных обратно вызывающей стороне - но это так ужасно, что я не хочу об этом думать). Точно так же, когда вы пишете модульный тест для классаC
, клиентом которогоB
вы будете, вы будете высмеивать что-то, что берет на себя рольB
, иA
вообще забудете о нем .Если вы этого не сделаете, это уже не модульный тест, а системный или интеграционный тест. Это тоже очень важно, но это совсем другой котелок с рыбой. Начнем с того, что они обычно требуют больше усилий для настройки и запуска, поэтому практически невозможно требовать их передачи в качестве предварительного условия для регистрации и т. Д.
источник
Выполнение модульных тестов с подключением к базе данных является вполне нормальной и обычной практикой. Просто невозможно создать
purist
подход, в котором все в вашей системе является зависимой инъекцией.Ключевым моментом здесь является тестирование на временной базе данных или тестирование только базы данных, а также создание максимально легкого процесса запуска для создания этой тестовой базы данных.
Для модульного тестирования в CakePHP есть вещи, которые называются
fixtures
. Светильники - это временные таблицы базы данных, созданные на лету для модульного теста. Светильник имеет удобные методы для их создания. Они могут воссоздать схему из производственной базы данных в тестовой базе данных, или вы можете определить схему, используя простую запись.Ключом к успеху в этом является не реализация бизнес-базы данных, а сосредоточение внимания только на аспекте кода, который вы тестируете. Если у вас есть модульный тест, который проверяет, что модель данных читает только опубликованные документы, тогда в схеме таблицы для этого теста должны быть только поля, требуемые этим кодом. Вам не нужно заново реализовывать всю базу данных управления контентом только для того, чтобы протестировать этот код.
Некоторые дополнительные ссылки.
http://en.wikipedia.org/wiki/Test_fixture
http://phpunit.de/manual/3.7/en/database.html
http://book.cakephp.org/2.0/en/development/testing.html#fixtures
источник
Где-то в вашей кодовой базе есть строка кода, которая выполняет фактическое действие по подключению к удаленной БД. Эта строка кода 9 раз из 10 является вызовом «встроенного» метода, предоставляемого библиотеками времени выполнения, специфичными для вашего языка и среды. Таким образом, это не «ваш» код, и вам не нужно его тестировать; для целей модульного теста вы можете быть уверены, что этот вызов метода будет выполнен правильно. То, что вы можете и должны по-прежнему тестировать в своем наборе модульных тестов, - это такие вещи, как обеспечение того, чтобы параметры, которые будут использоваться для этого вызова, были такими, как вы ожидаете, например, проверка правильности строки подключения или оператора SQL или имя хранимой процедуры.
Это одна из целей ограничения, заключающегося в том, что модульные тесты не должны покидать «песочницу» времени выполнения и зависеть от внешнего состояния. Это на самом деле довольно практично; Цель модульного теста - убедиться, что код, который вы написали (или собираетесь написать в TDD), ведет себя так, как вы думали. Код, который вы не написали, например, библиотека, которую вы используете для выполнения операций с базой данных, не должен входить в рамки какого-либо модульного теста по той простой причине, что вы его не написали.
В вашем наборе интеграционных тестов эти ограничения ослаблены. Теперь вы можетеСоздавайте тесты, которые касаются базы данных, чтобы убедиться, что код, который вы написали, хорошо сочетается с кодом, которого вы не делали. Однако эти два набора тестов должны оставаться разделенными, потому что ваш набор модульных тестов тем эффективнее, чем быстрее он выполняется (так что вы можете быстро проверить, что все утверждения, сделанные разработчиками относительно их кода, все еще выполняются), и почти по определению, интеграционный тест медленнее на порядок из-за добавленных зависимостей от внешних ресурсов. Позвольте сборщику-боту обрабатывать ваш полный комплект интеграции каждые несколько часов, выполняя тесты, которые блокируют внешние ресурсы, чтобы разработчики не давили друг другу на ноги, выполняя эти же тесты локально. А если ломается сборка, ну и что? Гораздо большее значение уделяется обеспечению того, чтобы сборщик никогда не провалил сборку, чем, вероятно, должно быть.
Теперь, насколько строго вы можете придерживаться этого, зависит от вашей точной стратегии подключения к базе данных и запроса к ней. Во многих случаях, когда вы должны использовать «доступную» среду доступа к данным, такую как объекты ADO.NET SqlConnection и SqlStatement, весь разработанный вами метод может состоять из вызовов встроенных методов и другого кода, который зависит от наличия соединение с базой данных, и поэтому лучшее, что вы можете сделать в этой ситуации, - это смоделировать всю функцию и довериться комплектам тестов интеграции. Это также зависит от того, насколько вы готовы разрабатывать свои классы, чтобы разрешить замену определенных строк кода для целей тестирования (например, предложение Тоби шаблона Template Template, который является хорошим, поскольку он допускает «частичные проверки»).
Если ваша модель персистентности данных опирается на код в вашем слое данных (например, триггеры, хранимые процессы и т. Д.), То просто нет другого способа выполнить код, который вы сами пишете, кроме разработки тестов, которые либо живут внутри слоя данных, либо пересекают граница между временем выполнения вашего приложения и СУБД. По этой причине пурист сказал бы, что эту модель следует избегать в пользу чего-то вроде ORM. Я не думаю, что пошел бы так далеко; даже в эпоху интегрированных в язык запросов и других проверенных компилятором зависимых от домена операций персистентности, я вижу значение в блокировании базы данных только для операций, предоставляемых хранимой процедурой, и, конечно, такие хранимые процедуры должны проверяться с использованием автоматизированных тесты. Но такие тесты не являются юнит- тестами. Они интеграция тесты.
Если у вас есть проблемы с этим различием, оно обычно основывается на высокой важности, придаваемой полному «охвату кода» или «охвату модульного теста». Вы хотите убедиться, что каждая строка вашего кода покрыта модульным тестом. Благородная цель на ее лице, но я говорю, фигня; этот менталитет поддается анти-паттернам, выходящим далеко за рамки этого конкретного случая, таким как написание безошибочных тестов, которые выполняют, но не осуществляютваш код. Эти типы конечных прогонов исключительно ради номеров покрытия более вредны, чем ослабление минимального покрытия. Если вы хотите убедиться, что каждая строка вашей кодовой базы выполняется каким-либо автоматическим тестом, то это просто; при расчете показателей покрытия кода включайте интеграционные тесты. Вы могли бы даже пойти еще дальше и изолировать эти спорные тесты «Itino» («Интеграция только в имени»), и между вашим модульным набором тестов и этой подкатегорией интеграционных тестов (которые все еще должны выполняться достаточно быстро) вы должны быть чертовски опасными почти близко к полному покрытию.
источник
Модульные тесты никогда не должны подключаться к базе данных. По определению, они должны тестировать одну единицу кода каждый (метод) в полной изоляции от остальной части вашей системы. Если они этого не делают, то они не являются модульным тестом.
Помимо семантики, существует множество причин, почему это полезно:
Модульные тесты - это способ проверить вашу работу. Они должны обрисовать в общих чертах все сценарии для данного метода, что обычно означает все различные пути через метод. Это ваша спецификация, к которой вы строите, аналогично двойной бухгалтерии.
То, что вы описываете, - это другой тип автоматизированного теста: интеграционный тест. Хотя они также очень важны, в идеале их будет гораздо меньше. Они должны проверить, что группа единиц должным образом интегрируется друг с другом.
Итак, как вы тестируете вещи с доступом к базе данных? Весь ваш код доступа к данным должен находиться на определенном уровне, чтобы код вашего приложения мог взаимодействовать с поддельными сервисами вместо реальной базы данных. Не должно заботиться о том, поддерживаются ли эти службы какими-либо базами данных SQL, данными испытаний в памяти или даже данными удаленных веб-сервисов. Это не их забота.
В идеале (и это очень субъективно) вы хотите, чтобы основная часть вашего кода была покрыта модульными тестами. Это дает вам уверенность, что каждый кусок работает независимо. Как только части построены, вам нужно собрать их вместе. Пример - когда я хэширую пароль пользователя, я должен получить именно такой вывод.
Предположим, что каждый компонент состоит примерно из 5 классов - вы хотите проверить все точки отказа в них. Это эквивалентно гораздо меньшему количеству тестов только для того, чтобы убедиться, что все подключено правильно. Пример - проверка, вы можете найти пользователя из базы данных, указав имя пользователя / пароль.
Наконец, вы хотите провести приемочные тесты, чтобы убедиться, что вы соответствуете бизнес-целям. Их еще меньше; они могут убедиться, что приложение работает и выполняет то, для чего оно было создано. Пример - учитывая данные теста, я должен быть в состоянии войти в систему.
Думайте об этих трех типах тестов как о пирамиде. Вам нужно много юнит-тестов для поддержки всего, а затем вы продолжаете свой путь оттуда.
источник
Метод Шаблон Шаблон может помочь.
Вы оборачиваете вызовы в базу данных
protected
методами. Чтобы протестировать этот класс, вы фактически тестируете поддельный объект, который наследует от реального класса соединения с базой данных и переопределяет защищенные методы.Таким образом, реальные вызовы базы данных никогда не проходят модульные тесты, это правильно. Но это только эти несколько строк кода. И это приемлемо.
источник
Тестирование с использованием внешних данных является интеграционным тестом. Юнит-тест означает, что вы тестируете только юнит. В основном это делается с вашей бизнес-логикой. Чтобы сделать ваш код тестируемым, вы должны следовать некоторым правилам, например, чтобы ваш модуль был независимым от другой части вашего кода. Во время модульного тестирования, если вам нужны данные, вам нужно принудительно внедрить эти данные с помощью внедрения зависимостей. Есть некоторые насмешливые и тупые рамки.
источник