Юнит-тестирование баз данных - PullRequest
32 голосов
/ 22 августа 2008

Прошлым летом я занимался разработкой базового приложения CRUD на ASP.NET/SQL Server, и одним из требований было модульное тестирование. Я столкнулся с некоторыми проблемами, когда попытался проверить базу данных. Насколько я понимаю, юнит-тесты должны быть:

  • без гражданства
  • независимо друг от друга
  • повторяется с теми же результатами, то есть без постоянных изменений

Эти требования противоречат друг другу при разработке базы данных. Например, я не могу проверить Insert (), не убедившись, что строки для вставки еще не созданы, поэтому мне нужно сначала вызвать Delete (). Но что, если их там еще нет? Затем мне нужно было бы сначала вызвать функцию Exists ().

Мое возможное решение включало в себя очень большие функции настройки (чёрт!) И пустой тестовый пример, который запускался бы первым и показывал, что установка прошла без проблем. Это приносит в жертву независимость испытаний при сохранении их безгражданства.

Другое решение, которое я нашел, - это обернуть вызовы функций в транзакцию, которую можно легко откатить, например XtUnit Роя Ошерова . Это работа, но она включает в себя другую библиотеку, другую зависимость, и кажется, что решение этой проблемы кажется слишком сложным.

Итак, что же сделал SO-сообщество, когда столкнулось с этой ситуацией?


tgmdbm сказал:

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

Так что, если я правильно прочитал это, на самом деле не существует способа эффективного модульного тестирования уровня доступа к данным. Или, может ли «модульный тест» уровня доступа к данным включать тестирование, скажем, SQL / команд, генерируемых классами, независимо от реального взаимодействия с базой данных?

Ответы [ 9 ]

25 голосов
/ 22 августа 2008

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

Как правило, устройство обычно не тестируется в базе данных. Обычно вы задействуете базу данных в интеграционных тестах.

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

11 голосов
/ 22 августа 2008

DBUnit

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

5 голосов
/ 22 августа 2008

Обычным решением внешних зависимостей в модульных тестах является использование фиктивных объектов, то есть библиотек, которые имитируют поведение реальных, с которыми вы тестируете. Это не всегда просто, а иногда требует некоторой изобретательности, но есть несколько хороших (бесплатных) библиотек-макетов для .Net, если вы не хотите «кататься». Два сразу приходят на ум:

Rhino Mocks имеет хорошую репутацию.

NMock - это другое.

Также доступно множество коммерческих библиотек. Частью написания хороших модульных тестов на самом деле является разработка вашего кода для них - например, используя интерфейсы там, где это имеет смысл, так что вы можете «насмехаться» над зависимым объектом, внедряя «поддельную» версию его интерфейса, которая тем не менее ведет себя предсказуемым образом, для целей тестирования.

В случае имитаций базы данных это означает «имитацию» собственного уровня доступа к БД с объектами, которые возвращают составленные объекты таблицы, строки или набора данных для ваших модульных тестов.

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

4 голосов
/ 22 августа 2008

Да, вам следует провести рефакторинг своего кода для доступа к репозиториям и службам, которые обращаются к базе данных, и затем вы можете смоделировать или заглушить эти объекты, чтобы тестируемый объект никогда не касался базы данных. Это гораздо быстрее, чем сохранять состояние базы данных и сбрасывать ее после каждого теста!

Я настоятельно рекомендую Moq в качестве основы для насмешек. Я использовал Rhino Mocks и NMock. Moq был настолько прост и решил все проблемы, которые у меня были с другими фреймворками.

2 голосов
/ 04 февраля 2009

Я объяснил технику, которую я использовал для этой самой ситуации здесь .

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

Единственная проблема, которую вы можете не найти "отличной", заключается в том, что я обычно делаю полный тест CRUD (не чисто с точки зрения модульного тестирования), но этот интеграционный тест позволяет вам увидеть код сопоставления CRUD + в действии. Таким образом, если он сломается, вы узнаете, прежде чем запустить приложение (это экономит мне массу работы, когда я пытаюсь идти быстро)

2 голосов
/ 04 февраля 2009

У меня был тот же вопрос, и я пришел к тем же базовым выводам, что и другие ответчики: не беспокойтесь о модульном тестировании фактического уровня связи БД, но если вы хотите провести модульное тестирование своих функций модели (чтобы убедиться правильно извлекаете данные, форматируете их и т. д.), используйте какой-то фиктивный источник данных и тесты настройки для проверки извлекаемых данных.

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

Образцы модульных тестов

1 голос
/ 25 апреля 2014

Тестирование слоя данных и базы данных вместе оставляет мало сюрпризов для дальнейшего проект. Но у тестирования на базе данных есть свои проблемы, главная из которых заключается в том, что вы проводите тестирование на состояние, разделяемое многими тестами. Если вы вставите строку в базу данных в одном тесте следующий тест также может видеть эту строку.
Что вам нужно, это способ откатить изменения, внесенные в базу данных.
Класс TransactionScope достаточно умен, чтобы обрабатывать очень сложные транзакции, а также вложенные транзакции, когда ваш тестируемый код фиксирует сам по себе локальная транзакция. Вот простой фрагмент кода, который показывает, как легко добавить возможность отката к ваши тесты:

    [TestFixture]
    public class TrannsactionScopeTests
    {
        private TransactionScope trans = null;

        [SetUp]
        public void SetUp()
        {
            trans = new TransactionScope(TransactionScopeOption.Required);
        }

        [TearDown]
        public void TearDown()
        {
            trans.Dispose();
        }

        [Test]
        public void TestServicedSameTransaction()
        {
            MySimpleClass c = new MySimpleClass();
            long id = c.InsertCategoryStandard("whatever");
            long id2 = c.InsertCategoryStandard("whatever");
            Console.WriteLine("Got id of " + id);
            Console.WriteLine("Got id of " + id2);
            Assert.AreNotEqual(id, id2);
        }
    }
1 голос
/ 22 августа 2008

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

0 голосов
/ 16 декабря 2008

Если вы используете LINQ to SQL в качестве ORM, вы можете создавать базу данных на лету (при условии, что у вас достаточно доступа к учетной записи, используемой для модульного тестирования). См. http://www.aaron -powell.com / blog.aspx? Id = 1125

...