Модульный тест «структура» метода? - PullRequest
0 голосов
/ 22 июня 2011

Извините за длинный пост ...

Хотя я знакомлюсь с коричневым полевым проектом, у меня есть сомнения относительно определенных наборов юнит-тестов и что думать.Допустим, у вас есть класс репозитория, в котором хранится хранимая процедура, и в руководстве разработчика, определенном наборе руководящих принципов (правил), описывается, как должен создаваться этот класс.Класс может выглядеть следующим образом:

public class PersonRepository
{
public PersonCollection FindPersonsByNameAndCity(string personName, string cityName)
{
    using (new SomeProfiler("someKey"))
    {
        var sp = Ioc.Resolve<IPersonStoredProcedure>();

        sp.addNameArguement(personName);
        sp.addCityArguement(cityName);

        return sp.invoke();
    }
} }

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

  • Конструктор для SomeProfiler с входным параметром "someKey" называется
  • Конструктор PersonStoredProcedure называется
  • Метод addNameArgument для хранимой процедуры вызывается с параметром personName
  • Метод addCityArgument для хранимой процедуры вызывается с параметром cityName
  • Метод invoke вызывается для хранимой процедуры -

Если это так, я потенциально буду тестировать всю структуру метода, кроме поведения.Моя первоначальная мысль, что это излишне.Однако, что касается практики кодирования, применяемой командой, эти тесты обеспечивают единую и «правильную» структуру и то, что следующий уровень вызывается правильно (от DAL до DB, от BLL до DAL и т. Д.).

В моем случае эти типы тестов выполняются для каждого слоя приложения.

Последующий вопрос - использование класса SomeProfiler для меня немного пахнет конвенцией. Вместо создания явных тестов для этого, можно ли создать тест в стиле соглашения с использованием статического анализа кода или unittest + отражением?

Заранее спасибо.

Ответы [ 2 ]

0 голосов
/ 23 июня 2011

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

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

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

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

Однако небольшое изменение существующего дизайна решает эту проблему, и "магия" исчезает:

public class PersonRepository : IPersonRepository
{
      private ConnectionManager _mgr;

      public PersonRepository(ConnectionManager mgr)
      {
         _mgr = mgr;
      }

      public PersonCollection FindPersonsByNameAndCity(string personName, string cityName)
      {
           using (var p = _mgr.CreateProfiler("somekey"))
           {
                 var sp = new PersonStoredProcedure(p);

                 sp.addArguement("name", personName);
                 sp.addArguement("city", cityName);

                 return sp.invoke();
           }
      }
}
0 голосов
/ 22 июня 2011

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

Возможно, вместо модульного тестирования вы должны использовать какой-нибудь инструмент, такой как FxCop / StyleCopили nDepend, чтобы убедиться, что все классы в конкретной сборке / dll имеют эти свойства.

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

...