Должен ли я использовать тестирование "стеклянной коробки", когда это приводит к * меньшему количеству * тестов? - PullRequest
3 голосов
/ 15 октября 2008

Например, я пишу тесты для CsvReader. Это простой класс, который перечисляет и разбивает строки текста. Его единственный raison d'être игнорирует запятые в кавычках. Это меньше, чем страница.

Проверяя класс "черным ящиком", я проверил такие вещи, как

  • Что если файл не существует?
  • Что если у меня нет разрешения на файл?
  • Что если в файле есть разрывы строк, отличные от Windows?

Но на самом деле все эти вещи являются бизнесом StreamReader. Мой класс работает, ничего не делая об этих случаях. Так что, по сути, мои тесты отлавливают ошибки, выдаваемые StreamReader, и поведение тестирования обрабатывается платформой. Такое ощущение, что много работы даром.

Я видел похожие вопросы

У меня вопрос: неужели я теряю смысл тестирования "стеклянной коробки", если я использую то, что знаю , чтобы избежать такой работы?

Ответы [ 6 ]

3 голосов
/ 15 октября 2008

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

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

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

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

3 голосов
/ 15 октября 2008

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

[EDIT] Пример внедрения зависимости (для получения дополнительной информации см. Ссылку выше)

Мы не используем внедрение зависимостей:

public class CvsReader {
   private string filename;
   public CvsReader(string filename)
   {
      this.filename = filename;
   }

   public string Read()
   {
      StreamReader reader = new StreamReader( this.filename );
      string contents = reader.ReadToEnd();
      .... do some stuff with contents...
      return contents;
   }
}

С внедрением зависимостей (конструктор) мы делаем:

public class CvsReader {
   private IStream stream;
   public CvsReader( IStream stream )
   {
      this.stream = stream;
   }

   public string Read()
   {
       StreamReader reader = new StreamReader( this.stream );
       string contents = reader.ReadToEnd();
       ...  do some stuff with contents ...
       return contents;
   }
}

Это позволяет легко тестировать CvsReader. Мы передаем экземпляр, реализующий интерфейс, от которого мы зависим, в конструкторе, в данном случае IStream. Из-за этого мы можем создать другой класс (возможно, фиктивный класс), который реализует IStream, но не обязательно выполняет файловый ввод-вывод. Мы можем использовать этот класс для передачи нашему читателю любых данных, которые мы хотим, без привлечения какой-либо базовой структуры. В этом случае я бы использовал MemoryStream, так как мы просто читаем из него. Однако мы хотели бы использовать фиктивный класс и предоставить ему более богатый интерфейс, который позволяет нашим тестам настраивать ответы, которые он дает. Таким образом, мы можем тестировать код, который пишем, и вообще не задействовать базовый код платформы. В качестве альтернативы мы могли бы также передать TextReader, но обычный шаблон внедрения зависимостей использует интерфейсы, и я хотел показать шаблон с интерфейсами. Возможно, лучше передать TextReader, поскольку приведенный выше код все еще зависит от реализации StreamReader.

2 голосов
/ 15 октября 2008

Да, но это было бы строго для целей модульного тестирования:

Вы можете абстрагировать вашу реализацию CSV-ридера от любого конкретного StreamReader, определив интерфейс абстрактного Stream Reader и протестировав свою собственную реализацию с помощью имитатора потока, который реализует этот интерфейс. Ваш пробный читатель, очевидно, будет невосприимчив к таким ошибкам, как несуществующие файлы, проблемы с правами доступа, различия в ОС и т. Д. Таким образом, вы полностью протестируете свой собственный код и сможете достичь 100% покрытия кода.

1 голос
/ 26 августа 2010

Просто к вашему сведению, если это .NET, вам не следует изобретать велосипед.

Для C #

Добавить ссылку на Microsoft.VisualBasic Используйте фантастический класс Microsoft.VisualBasic.FileIO.TextFieldParser () для обработки ваших потребностей в разборе CSV.

Microsoft уже проверила это, так что вам не придется.

Наслаждайтесь.

1 голос
/ 16 октября 2008

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

Хотя markj и прав, что тесты должны охватывать «наблюдаемое внешнее поведение» класса, я думаю, уместно рассмотреть , где - это поведение. Если это поведение через наследование от другого (предположительно, модульно проверенного) класса, то я не вижу никакой выгоды в добавлении ваших собственных unit тестов. OTOH, если это поведение через композицию, то может оправдать некоторые тесты, чтобы убедиться, что проходы работают правильно.

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

0 голосов
/ 15 октября 2008

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

...