Стратегии создания тестовых данных, соответствующих классу/схеме, для тестов, управляемых данными

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

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

То, что я хотел бы нажать, - это тесты Data Driven. Я думаю, что мы должны иметь возможность загружать наши тестовые данные из файлов или, возможно, даже генерировать их "на лету" из схемы (хотя я бы рассматривал это только на лету, если бы я мог генерировать любую возможную конфигурацию объекта, и это число конфигурации были небольшими). И есть моя проблема.

Мне еще предстоит найти хорошую стратегию для генерации тестовых данных для объектов CL # CLR.

Я просмотрел данные XML из XSD и затем загрузил их в свои тесты с помощью DataSourceAttribute. Казалось, что это хороший подход, но я столкнулся с проблемами, генерирующими файлы XSD. xsd.exe падает, потому что наши классы имеют члены интерфейса. Я также попытался использовать svcutil.exe на нашей сборке, но поскольку наш код является монолитным, вывод является огромным и сложным (многие взаимозависимые файлы .xsd).

Каковы другие методы генерации тестовых данных? В идеале генератор должен следовать схеме (возможно, xsd, но предпочтительно самому классу) и может быть сценарием. Технические примечания (не уверен, что это даже актуально, но это не может повредить):

  • Мы используем инфраструктуру модульного тестирования Visual Studio (определенную в Microsoft.VisualStudio.TestTools.UnitTesting).
  • Мы используем RhinoMocks

Спасибо

Дополнительные сведения

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

Я хотел бы иметь возможность генерировать большое количество конфигураций этого унаследованного Entity и запускать их через адаптер. Чем больше количество конфигураций, тем более вероятным будет мой тест, когда следующий разработчик (не обращая внимания на 90% приложения) изменит схему устаревшего объекта.

ОБНОВЛЕНИЕ

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

Я просто перечитал свой вопрос и заметил, что на самом деле я изначально прошу о случайном рождении. Я удивлен, что я прошу об этом! Я обновил вопрос, чтобы исправить это. Извините за путаницу.

15
03 дек. '13 в 12:09
источник поделиться
5 ответов

Вам нужен инструмент, например NBuilder (http://code.google.com/p/nbuilder).

Это позволяет вам описывать объекты, а затем генерировать их. Это отлично подходит для модульного тестирования.

Вот очень простой пример (но вы можете сделать его как можно более сложным):

var products = Builder<Product>
                   .CreateListOfSize(10)
                   .All().With(x => x.Title = "some title")
                   .And(x => x.AnyProperty = RandomlyGeneratedValue())
                   .And(x => x.AnyOtherProperty = OtherRandomlyGeneratedValue())
                   .Build();
8
03 дек. '13 в 12:14
источник

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

Я работал с клиентом, у которого была аналогичная проблема, и они закончили тем, что хранили свои объекты как JSON и десериализовали их, ожидая, что их будет проще поддерживать и расширять. Это не так. Вы знаете, что не получается при редактировании JSON? Проверка синтаксиса времени компиляции. Они только что закончили тесты, из-за JSON, которые не смогли десериализоваться из-за синтаксических ошибок.

Одна вещь, которую вы можете сделать, чтобы уменьшить вашу боль, - это код для небольших интерфейсов. Если у вас есть гигантский объект с тонны свойств, данный метод, который вы хотите протестировать, вероятно, понадобится только для небольшого количества. Поэтому вместо вашего метода, принимающего SomeGiantClass, возьмите класс, реализующий ITinySubset. Работа с меньшим подмножеством сделает гораздо более очевидным то, что нужно заполнить, чтобы ваш тест имел какую-либо достоверность.

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

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

3
20 дек. '13 в 16:47
источник

Это немного отличается от того, о чем вы говорите, но посмотрели ли вы на Pex? Pex будет пытаться генерировать входы, которые охватывают все пути вашего кода.

http://research.microsoft.com/en-us/projects/Pex/

2
19 дек. '13 в 23:49
источник

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

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

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

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

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

2
20 дек. '13 в 15:46
источник

На самом деле, существует способ Microsoft выражать экземпляры объектов в разметке, и это XAML.

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

Почему я сделал бы это? потому что проект Visual Studio автоматически предоставит вам синтаксис XAML и, возможно, поддержку intellisense при добавлении этого файла.

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

Для справки, посмотрите:

Жаль, что я не смогу показать вам что-то, сделанное мной по этому вопросу, но я не могу.

1
20 дек. '13 в 19:05
источник

Посмотрите другие вопросы по меткам или Задайте вопрос