Подтвердить что ты не робот

Базы данных для тестирования модулей

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

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

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

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

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

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


tgmdbm сказал:

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

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

4b9b3361

Ответ 1

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

Обычно вы не проверяете базу данных unit. Обычно вы используете базу данных в тестах интеграции.

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

Ответ 2

DBunit

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

Ответ 3

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

Rhino Mocks - это та хорошая репутация.

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

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

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

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

Ответ 4

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

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

Ответ 5

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

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

Unit Test Шаблоны

Ответ 6

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

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

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

Ответ 7

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

Ответ 8

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

    [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);
        }
    }

Ответ 9

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