Как избежать дублирования тестового кода - PullRequest
5 голосов
/ 10 июня 2009

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

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

Итак, каковы рекомендации по структурированию тестовых проектов для тестирования Unit Vs Integration? Я так понимаю, некоторые люди предпочитают отдельные сборки для агрегата и интеграции?

Как можно было бы разделить общий тестовый код между двумя сборками? Должен ли я создать третью сборку, которая содержит все абстрактные тестовые классы, и пусть модуль и интеграция наследуются? Я ищу максимальное повторное использование ...

Я слышу много шума о Dependency Injection (StructureMap). Как можно использовать такой инструмент в данной настройке Unit + Integration Test?

Кто-нибудь может поделиться мудростью? Спасибо

Ответы [ 7 ]

1 голос
/ 28 августа 2009

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

1 голос
/ 10 июня 2009

Я не думаю, что вы должны физически разделить их. Хорошим решением является размещение над вашими тестами атрибута Microsoft.TeamFoundation.PowerTools.Tasks.CategoryAttribute для определения регулярных и интеграционных тестов. При запуске тестов (даже с MSBuild) вы можете выбрать запуск только тех тестов, которые вам интересны.

В качестве альтернативы вы можете поместить их в отдельные пространства имен.

0 голосов
/ 22 сентября 2014

Если единственное различие между многими вашими модульными тестами и соответствующими интеграционными тестами заключается в том, что последние используют «реальные» ресурсы, а не фальшивые (поддельные), один из подходов следующий:

  1. Сделайте флаг is_unit_test доступным для вашего тестового класса извне
  2. В настройках класса делает доступными фиктивные или реальные ресурсы в зависимости от флага. Например, если вам нужно использовать DB API, который является реальным (экземпляр класса DBreal) или поддельным (экземпляр класса DBfake), ваша инициализация может выглядеть как if is_unit_test then this.dbapi = new DBfake else this.dbapi = new DBreal. (DBreal и DBfake должны соответствовать одному интерфейсу, назовем его DBapi.)
  3. С точки зрения ваших методов тестирования шаг 2 составляет (вручную) Внедрение зависимости : метод не знает, какой класс на самом деле реализует свою зависимость (API БД). Скорее зависимость вводится в метод извне.
  4. Если для ваших тестовых примеров требуется API БД, они используют this.dbapi
  5. Теперь вы выполняете один и тот же класс тестирования с флагом, установленным для модульного тестирования, и без флага, установленного для интеграционного тестирования. (Как сделать флаг доступным, зависит от вашей среды модульного тестирования.)
  6. Очевидно, что тот же подход можно использовать, если вам нужно более одного ресурса в тестовом классе.
  7. Некоторые люди найдут явное if в шаге 2 безобразным. Чтобы сделать его более «элегантным», вы можете вместо этого использовать контейнер Inversion of Control (IoC) (в Java, например, Spring или PicoContainer), чтобы полуавтоматизировать внедрение зависимостей. Инициализация будет выглядеть как this.dbapi = myContainer.create(DBapi).
  8. В простых случаях контейнер IoC только усложнит ситуацию, поскольку настройка контейнера не тривиальна, требует обучения, открывает возможность появления нового класса ошибок и включает дополнительные файлы.
  9. В более сложных случаях однако, контейнер упрощает задачу, потому что если создание ваших ресурсов требует еще других ресурсов, контейнер также позаботится об их инициализации, и сложность действительно уменьшится. Но если вы действительно не доберетесь туда, я предлагаю KISS .
  10. Если у вас нет веских причин для отдельных сборок, они нарушают KISS. Я предлагаю сначала подождать по этой причине.

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

0 голосов
/ 11 июня 2009

После некоторых экспериментов вы можете использовать методы испытаний повторно:

    public abstract class TestBase
    {
        [TestMethod]
        public void BaseTestMethod()
        {
            Assert.IsTrue(true);
        }
    }


    [TestClass]
    public class UnitTest : TestBase
    {
    }

    [TestClass]
    public class IntegrationTest : TestBase
    {
    }

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

Вы должны иметь возможность создать параметризованный конструктор в базовом классе, чтобы вводить ваши макеты или ресурсы.

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

Спасибо за советы, чел!

0 голосов
/ 10 июня 2009

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

Это означает, что Services.Test может ссылаться на Domain.Test, что имеет смысл для нас, поскольку Службы ссылаются на Domain в фактическом коде.

В пересчете на куски многократного использования

  • Построители - Они обеспечивают свободный интерфейс для создания наиболее важных / сложных объектов в нашей области. Они живут в основной тестовой папке для нашего домена. На нашу тестовую сборку домена ссылаются все остальные тестовые сборки.

  • Матери - Они вставляют данные в базу данных. Они возвращают идентификатор для вставленной строки, который может быть использован для загрузки объекта, если требуется. Они находятся в главной тестовой папке для наших сервисов.

  • Помощники - это ребята, которые делают маленькие вещи на протяжении всего нашего тестирования. Например, мы предпочитаем разрешить доступ к коллекциям через IEnuermable, поэтому у нас есть CollectionHelper.AssertCountIsEqualTo <_T> (int count, IEnumerable <_T> collection, string message), который оборачивает IEnumerable в List и устанавливает количество. Все наши помощники живут в общем тесте, на который ссылается каждый другой тест.

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

0 голосов
/ 10 июня 2009

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

0 голосов
/ 10 июня 2009

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...