UnitTesting класс, который возвращает сложный набор данных - PullRequest
5 голосов
/ 19 октября 2010

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

Я уже заказал книгу Майкла Пера , мне нравится Рефакторинг Фаулера , и я сделал несколько примеров проектов с DUnit .

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

Почти 100% кода, над которым я работаю, содержат бизнес-логику в пользовательском интерфейсе, более того, все это процедурное программирование (за некоторыми исключениями). Приложение запустилось так быстро и грязно и продолжило как таковое.

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

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

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

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

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

Следующие пункты мне не понятны:

1) как проверить сложный набор данных? Должен ли я запустить работающее приложение, сохранить один набор результатов в xml и написать тест, в котором я использую TClientDataSet, содержащий эти данные xml?

2) Сколько мне нужно заботиться о TSchedulerData? Я имею в виду, что я не уверен на 100%, что буду использовать TSchedulerData, может быть, я буду придерживаться набора данных, так как мысль о создании сложных тестов, которые будут отброшены через 2 недели, не подходит для DUnitNewbee. В любом случае, вероятно, так оно и есть. Я не могу представить количество ошибок, с которыми я столкнулся бы без теста.

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

Ответы [ 2 ]

2 голосов
/ 19 октября 2010

Ваша конечная цель - разделить пользовательский интерфейс, хранилище данных и бизнес-логику на отдельные слои.

Очень сложно протестировать пользовательский интерфейс с помощью автоматических сред тестирования. В конечном итоге вы захотите отделить как можно больше бизнес-логики от пользовательского интерфейса. Это может быть выполнено с использованием одного из различных шаблонов Model / View / *. Я предпочитаю пассивное представление MVP, которое пытается сделать интерфейс пользователя не более чем интерфейсом. Если вы используете Dataset MVP, лучше подойдет контролирующий контроллер.

Хранилище данных должно иметь собственный набор тестов, но они отличаются от модульных тестов (хотя вы можете использовать одну и ту же платформу модульного тестирования), и их обычно меньше. Вы можете избежать неприятностей с этим, потому что большая часть тяжелой работы выполняется сторонними компонентами данных и dbms (в вашем случае T * Dataset). Это интеграционные тесты. По сути, убедитесь, что ваш код хорошо сочетается с кодом поставщика. Также необходимо, если у вас есть хранимые процедуры, определенные в БД. Они намного медленнее, чем юнит-тесты, и их не нужно запускать так часто.

Бизнес-логика - это то, что вы хотите проверить больше всего. Каждое вычисление, цикл или ветвь должны иметь хотя бы один тест (желательно больше). В унаследованном коде эта логика часто напрямую касается интерфейса и базы данных и выполняет несколько функций в одной функции. Здесь Метод извлечения - ваш друг. Хорошие места для извлечения методов:

for I:=0 to List.Count - 1 do
begin
  //HERE
end;

if /*HERE if its a complex condition*/ then
begin
  //HERE
end
else
begin
  //HERE
end

Answer := Var1 / Var2 + Var1 * Var3; //HERE

Когда вы сталкиваетесь с одной из этих точек извлечения

  1. Определите, как должна выглядеть подпись метода для вашего нового метода: имя метода, параметры, возвращаемое значение.
  2. Напишите тест, который вызывает его и проверяет ожидаемый результат.
  3. Извлеките метод.

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

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

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

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

2 голосов
/ 19 октября 2010

Я бы сказал, что подходите к нему целенаправленными детскими шагами.

Шаг # 1: Всегда следует проводить некоторые тесты в вашем районе вторжения. В вашем случае почувствуйте, что делает приложение. Из того, что я прочитал, похоже, что преобразователь данных. Поэтому потратьте время, чтобы понять все (или наиболее важные, если все неосуществимо) комбинации входных данных и соответствующих графиков вывода. Запишите их как тесты. Убедитесь, что все тесты пройдены.

Шаг № 2: Теперь попытайтесь провести рефакторинг. Переместите блоки кода в связные классы и т. Д. Под защитой регрессионной сети.

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

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