Форум Flasher.ru
Ближайшие курсы в Школе RealTime
Список интенсивных курсов: [см.]  
  
Специальные предложения: [см.]  
  
 
Блоги Правила Справка Пользователи Календарь Поиск рулит! Сообщения за день Все разделы прочитаны
 

Вернуться   Форум Flasher.ru > Блоги > surlac

Оценить эту запись

Пресловутый TDD

Запись от surlac размещена 24.01.2011 в 20:19
Обновил(-а) surlac 27.01.2011 в 13:24 (global refactoring)

... а функциональность тем временем росла не по дням, а по часам. И решил я, как выходец из мира Java, наконец воспользоваться пресловутым Test Driven Design, который так часто выручал меня.

Введение.
Разработка через тестирование (TDD) - это один из способов получить изящный, гибкий и понятный код, который легко модифицировать, который корректно работает и который не подкидывает своим создателям неприятных сюрпризов [1]. В основе методики TDD (кстати, данная методика входит в состав очень практически направленной методологии управления проектами, как Экстремальное Программирование - XP) лежит парадоксальная идея - "тестируйте программу до того, как она написана". Бессмыслица? Не спешите делать выводы о нецелесообразности идеи вообще и неприменимости её к разработке для Flash-платформы в частности.

Применимость.
Как и любая другая методика XP, TDD не претендует на звание панацеи от всех болезней{1}. Определенно, существуют задачи, которые невозможно решить только при помощи тестов. Так что не спешите закрывать QA-отделы Ваших предприятий .
Тесты возможно писать почти до бесконечности, углубляясь с помощью декомпозиции в детали. Но каков практически значимый предел? Phlip Plumlee: "Пишите тесты до тех пор, пока страх не превратится в скуку". То есть, очень индивидуально для каждого проекта. Я лично считаю, что нужно писать до тех пор, пока тесты не покроют весь критически важный для проекта код, то есть всю функциональность с приоритетами Blocker и Critical, если говорить в терминах систем отслеживания ошибок.

Мотивация.
Как часто Вам приходилось бывать в ситуации, когда буквально за 5 минут до показа продукта заказчику функциональность модуля "падала" из-за поспешных "коммитов" разработчиков другого модуля. С нашей командой такое бывало несколько раз, пока мы не настроили build-сервер в связке с SVN-репозиторием, который через ANT запускал пачку тестов на проверку самих модулей и их взаимодействия. В итоге, да, сроки изначально растягивались, так как нам приходилось писать не только модуль, но и тесты для него. Но это окупалось за счет предсказуемости, надежности и сохранности наших нервов в конце концов!

Реализация.
И так, постепенно переходим от теоретических выкладок к практическим мероприятиям.
Платформа - Flash (повлияет только на выбор инструмента).
В качестве инструмента можно использовать как ASUnit, так и FlexUnit {2}. Я не углублялся в сравнение этих двух тулз, но выбор пал, чисто субъективно, на ASUnit из-за обвинения разрабов FlexUnit в нарушении конвенции xUnit.
Для удобства запуска тестов и разделения логики тестов от тестируемой функциональности (см. п. "2. Файл с настройками для сценария") был выбран Apache ANT {3}. Т.к. я с ним достаточно долго работал - тоже субъективно {4}.

И так, далее в этой статье:
  • покажем, как прикрутить ASUnit с помощью ANT
  • реализуем небольшой примерчик
Основная идея: пишем "проекто-зависимый" файл настроек build.properties для ant-сценария build.xml и сам сценарий. Создаем TestCase, объявляем его в Suite и запускаем с помощью TextCore в Runner. Поехали!

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

Код:
<property file="build.properties"/>
<!-- flex resources -->
<property name="mxmlc.jar" location="${flex.dir}/lib/mxmlc.jar"/>
<property name="flex.config" location="${flex.dir}/frameworks/flex-config.xml"/>
<property name="flex.lib" location="${flex.dir}/frameworks/libs"/>
<property name="flextask.jar" location="${flex.dir}/ant/lib/flexTasks.jar"/>
<property name="compc.jar" location="${flex.dir}/lib/compc.jar"/>
<property name="FLEX_HOME" value="${flex.dir}"/>
 
<!-- testing -->
<target name="tests">
 <taskdef resource="flexTasks.tasks" classpath="${flextask.jar}"/>
 
 <mxmlc file="${test.main}"
     output="${tests.output}"
     incremental="true"
     debug="false"
     static-link-runtime-shared-libraries="true">
 
  <source-path path-element="${src.dir}"/>
  <source-path path-element="${tests.dir}"/>
  <source-path path-element="${asunit.dir}"/>
  <load-config filename="${flex.config}"/>
  <library-path dir="${flex.lib}" append="true">
   <include name="flex.swc"/>
  </library-path>
  <library-path dir="${libs.test.dir}" append="true">
   <include name="*.swc"/>
  </library-path>
  <keep-as3-metadata name="Inject"/>
  <keep-as3-metadata name="Test"/>
  <keep-as3-metadata name="Suite"/>
  <keep-as3-metadata name="Before"/>
  <keep-as3-metadata name="BeforeClass"/>
  <keep-as3-metadata name="After"/>
  <keep-as3-metadata name="AfterClass"/>
  <keep-as3-metadata name="RunWith"/>
  <keep-as3-metadata name="Ignore"/>  
 </mxmlc>
 
 <exec executable="cmd.exe" osfamily="windows">
  <arg line='/C start ${tests.output}'/>
 </exec>
</target>

2. Файл с настройками для сценария. build.properties.
Как видно их сценария, для тестов используется две директории src.dir и tests.dir. Первый определяет что тестировать, второй - как тестировать. Это используется для отделения логики тестирования, от самих модулей.
Код:
######################################
## project properties
######################################
source.file=ru/pkg/Example.as
output.file=Example.swf
 
deploy.dir=deploy
source.dir=src
 
######################################
## tools
######################################
 
# where you installed flex:
flex.dir=D:/flex_sdk_4.1
 
# browsers
firefox=C:/Program Files/Mozilla Firefox/firefox.exe
ie=C:/Program Files/Internet Explorer/iexplore.exe
 
compiler=bin/mxmlc.exe
saplayer=player/debug/SAFlashPlayer.exe
flashdevelop=D:/FlashDevelop/FlashDevelop.exe
 
# Build locations
src.dir=${basedir}/src
package.dir=ru/pkg
libs.dir=${basedir}/libs
output.dir=${basedir}/bin
output.swc=${output.dir}/${project.name.versioned}.swc
 
# testing
tests.dir=${basedir}/tests
tests.output=bin/tests.swf
libs.test.dir=D:/asunit/asunit-4.0/lib/
asunit.dir=D:/asunit/asunit-4.0/src/
test.main=${tests.dir}/${package.dir}/AllTestsRunner.as
docs.dir=${basedir}/docs
3. Test-case
Сам тест-кейс. Хранит методы для тестирования. Рекомендуется наследовать от TestCase, т.к. он агрегирует класс Assert и оборачивает его методы.
В качестве примера, протестируем обработчик абсолютно упругого столкновения RecoilProcessor для шаров произвольного радиуса. Создадим искусственную ситуацию, когда шарик движется по оси OX с заданной скоростью и сталкивается с вертикальной стеной.
Код AS3:
package ru.pkg.core
{
 import asunit.framework.*;
 import ru.pkg.core.*;
 
 public class RecoilProcessorTest extends TestCase
 {  
  private var instance:RecoilProcessor; 
 
  //--------------------------------------------------------------------------
  //
  //  Methods
  //
  //--------------------------------------------------------------------------
 
  //--------------------------------------------------------------------------
  //  public methods
  //--------------------------------------------------------------------------  
 
  [test]
  public function testOne():void
  {
    // создаем экземпляр шарика
    var circle:FloatingCircle = new FloatingCircle(0xFFFFFF, 10);
    // задаем скорость по осям
    circle.speedByX = 5;
    circle.speedByY = 0;
    // устанавливаем искусственно точку столкновения (point of collision)
    var poc:Point = new Point(10, 0);
    // RecoilProcessor высчитывает новые скорости по осям и возвращает в виде Point.
    // т.к. шарик двигался горизонтально и столкнулся с крайней правой точкой шарика,
    // то отскок произойдет в противоположную сторону со скоростью -5 по OX и 0 по OY.
    var resultPoint:Point = instance.calcNewSpeed(circle.speedByX, circle.speedByY, poc.x, poc.y, circle.radius);
 
    // ВНИМАНИЕ! Проверка. Результаты вычисления новых скоростей RecoilProcessor'а сравниваются с 
    // заранее верными значениями. Если возвращаемые значения совпадают с -5 и 0 - тест 
    // завершается успешно, иначе - Fail.
    assertEquals(-5, resultPoint.x);
    assertEquals(0, resultPoint.y);
  }
 
  //--------------------------------------------------------------------------
  //  protected methods
  //--------------------------------------------------------------------------
 
  protected override function setUp():void
  {
   // Здесь происходит первоначальная настройка теста.                         
   instance = new RecoilProcessor();
  }
 
  protected override function tearDown():void
  {
   // Убираем за собой после выполнения теста.
   instance = null;
  }
 }
}

4. Suite
Suite-класс, хранящий все тест-кейсы. Насколько я понял, движок asunit создает TestCase объекты, объявленные здесь и инжектит их в переменные объекта Suite.
Код AS3:
package ru.pkg
{
 import ru.pkg.core.RecoilProcessorTest;
 
 [Suite]
 public class AllTests
 {
  public var _RecoilProcessorTest:RecoilProcessorTest; 
 }
}
5. Runner
Главный класс для запуска Suite. Должен быть наследником DisplayObject, т.к. выводит результаты на экран. TextCore настраивает объектами классов Suite и TextPrinter ядро asunit - AsUnitCore.
Код AS3:
package ru.pkg
{
 import asunit.core.TextCore;
 import flash.display.Sprite;
 
 [SWF(width='1000',height='800',backgroundColor='#333333',frameRate='31')]
 public class AllTestsRunner extends Sprite
 {
  private var core:TextCore;
 
  public function AllTestsRunner()
  {
   core = new TextCore();
   core.start(AllTests, null, this);  
  }
 }
}
Результат выполнения теста:


Заключение.
Это конечно же лишь тонкий намек на то, что на самом деле умеет ASUnit, как инструмент методики TDD. Лишь введение в TDD со стороны Flash-платформы и небольшой толчок в сторону применения методик Экстремального Программирования.

Перспективы.
В перспективе рассмотреть такие вещи, как:
  • асинхронное тестирование - для тестирования клиент-серверного взаимодействия, например.
  • использование ASUnit в связке с build-сервером для непрерывной интеграции.
  • текущее положение дел FlexUnit и его применение.

Литература:
1. К. Бек "Экстремальное программирование: разработка через тестирование".
2. L. Cripsin, J. Gregory "Agile testing: a practical guide for testers and agile teams".

Сноски:
1. Инфицированная тестами система - определение, придуманное Эрихом Гаммой (Erich Gamma).
2. Нужно заметить что у обоих претендентов "ноги растут" из старого доброго JUnit для Java, это просто порты на AS3.
3. Не будем здесь описывать тонкости настройки ANT, для этого существует множество хорошей документации.
4. Я считаю, что для Flash-платформы, инструмента ANT будет хватать с лихвой. Врядли тут понадобится централизованное хранение либ, как в случае Maven, для того, чтобы не пихать либы в SVN, а хранить их в отдельном глобальном или локальном mvn-репозитории.
Изображения
Тип файла: jpg asunit_results.jpg (83.1 Кб, 866 просмотров)
Всего комментариев 10

Комментарии

Старый 24.01.2011 22:07 nOobCrafter вне форума
nOobCrafter
Спасибо! Занятно будет почитать) а то что то эта аббревиатура начала все чаще мелькать перед глазами.
Старый 24.01.2011 22:40 dimarik вне форума
dimarik
 
Аватар для dimarik
Бессистемная статья. Расскажите, пожалуйста, зачем заворачивать UnitTest в ant? Вы даже толком (ссылками) не объяснили про этот инструмент. Зачем даете описание опций компилятора? Где объяснение непоняток? Что такое "функционал", наконец?

Нужно работать.
Старый 25.01.2011 03:53 Rzer вне форума
Rzer
 
Аватар для Rzer
Вот мне, как человеку не знающему зачем TDD нужен, после такой статьи ещё меньше захотелось знать об этом. Меня пугает, то, что для сравнения пяти с пятью нужно столько знать.
Старый 25.01.2011 13:49 †‡Paladin‡† вне форума
†‡Paladin‡†
Во флеше TDD не нужен. Слишком низкие стандарты качества.
Старый 25.01.2011 17:23 Котяра вне форума
Котяра
 
Аватар для Котяра
Странное заявление. Как будто технология задаёт стандарты качества? Скорее во флеше TDD мало кто используют ибо не умеют и не удобно для конкретных проектов.
По большому счёту я хоть и пробовал использовать Unit тесты, мне они не помогали, а только мешали ибо моя область деятельности клиент/сервер и соответственно основные ошибки трудно отлавливаются на момент компиляции. Тут в дело вступает логирование и багрепорты.
В качестве юниттестеров выступают люди, автоматизация такого тестирования может быть дороже и сложнее самого тестируемого контента.

А насчёт статьи, хотелось бы больше информации об ант, как ставить, как работать в FB/FD/Idea, как писать свои сценарии, синтаксис, настройка и т.п.
Обновил(-а) Котяра 25.01.2011 в 17:53
Старый 25.01.2011 17:37 i.o. вне форума
i.o.
 
Аватар для i.o.
Цитата:
хотелось бы больше информации об ант, как ставить, как работать в FB/FD/Idea, как писать свои сценарии, синтаксис, настройка и т.п.
Полностью поддерживаю.
Старый 25.01.2011 20:24 surlac вне форума
surlac
 
Аватар для surlac
Благодарю за толковые комментарии, особенно dimarik.

Цитата:
Сообщение от Котяра
А насчёт статьи, хотелось бы больше информации об ант, как ставить, как работать в FB/FD/Idea, как писать свои сценарии, синтаксис, настройка и т.п.
Это очень легко устроить . Только в другой статье. В этой статье я старался делать упор на практической реализации TDD во Flash, и ANT тут используется только для удобства (разделение на test и src) и возможности дальше капнуть в сторону непрерывной интеграции. Посмотрите тут, отличная дока.

Цитата:
Сообщение от Rzer
Вот мне, как человеку не знающему зачем TDD нужен, после такой статьи ещё меньше захотелось знать об этом. Меня пугает, то, что для сравнения пяти с пятью нужно столько знать.
Ok, вполне Вас понимаю. Здесь нужно осознавать, что TDD является лишь инструментом XP, который в свою очередь является практической методологией, дополняющей гибкое программирование (Agile например). Гибкое программирование же является частью более глобальной дисциплины, как Управление Проектами. Теперь, чтобы объяснить Вам зачем нужен TDD, можно просто описать цель управления проектами. Если степени детальности будет недостаточно, можно посмотреть цель гибкого программирования и так далее рекурсивно вглубь до полного понимания.
Другое дело, что само управление проектами и TDD в частности имеет максимум эффективности при достаточно больших командах (3 до 12 чел - см. "Scrum and XP from the Trenches") и при циклах разработки от 2 до 6 недель (т.е достаточно большой проект).
Согласен с Вами в том, что если проект небольшой - максимума эффективности от TDD мы не добьемся.


Цитата:
Сообщение от †‡Paladin‡†
Во флеше TDD не нужен. Слишком низкие стандарты качества.
Нужен/не нужен определяется индивидуально для каждого проекта. Само собой Вы можете напрочь отказаться от инструментов, позволяющих успешно завершать проекты и делать всё по-своему. Без проблем. И это работает, но только для небольших проектов и команд.
Старый 26.01.2011 16:43 †‡Paladin‡† вне форума
†‡Paladin‡†
Цитата:
Как будто технология задаёт стандарты качества?
Я говорил про индустрию. Стандарты качества диктует пользователь.
Цитата:
Нужен/не нужен определяется индивидуально для каждого проекта. Само собой Вы можете напрочь отказаться от инструментов, позволяющих успешно завершать проекты и делать всё по-своему. Без проблем. И это работает, но только для небольших проектов и команд.
Приведите пример российского флеш проекта, который, как вы считаете, не получится завершить без TDD.
Обновил(-а) †‡Paladin‡† 26.01.2011 в 16:46
Старый 26.01.2011 23:33 expl вне форума
expl
Цитата:
Нужен/не нужен определяется индивидуально для каждого проекта. Само собой Вы можете напрочь отказаться от инструментов, позволяющих успешно завершать проекты и делать всё по-своему. Без проблем. И это работает, но только для небольших проектов и команд.
Даже такой большой проект, как клон WorldOfWarkraft в бравзере вполне себе писался без TDD. Старый функционал только постоянно отваливается.

Цитата:
Вот мне, как человеку не знающему зачем TDD нужен, после такой статьи ещё меньше захотелось знать об этом. Меня пугает, то, что для сравнения пяти с пятью нужно столько знать.
Установи FlashBuilder 4 Premium - нифига знать не надо, тесты создаются в 2 клика, запускаются в 4 кнопки.

Автору:
От теста "assertEquals(5, 5)", даже не "assert(2 + 2, 4)", сразу переходить к непрерывной интеграции?? Не слишком ли круто?
Может лучше рассмотреть создание конкретного класса с помощью тестов?

Оффтоп:
surlac, а как вы ant-файлы запускаете, через коммандную строку или в IDE горячую клавишу настроили?
Обновил(-а) expl 26.01.2011 в 23:52
Старый 27.01.2011 18:38 surlac вне форума
surlac
 
Аватар для surlac
Цитата:
Сообщение от expl
От теста "assertEquals(5, 5)", даже не "assert(2 + 2, 4)", сразу переходить к непрерывной интеграции?? Не слишком ли круто?
Это не так сложно, как Вам кажется . Сложнее всего начать пользоваться чем-то новым. Эта статья как раз направлена на то, чтобы описать как начать пользоваться TDD для Flash. Нужно оно Вам или нет решать опять же Вам.
Цитата:
Сообщение от expl
Может лучше рассмотреть создание конкретного класса с помощью тестов?
Хороший совет. Подправил немного п. 3. Теперь тестируем обработчик столкновений.

Цитата:
Сообщение от expl
Оффтоп:
surlac, а как вы ant-файлы запускаете, через коммандную строку или в IDE горячую клавишу настроили?
Цель ant-сценария я запускаю через командную строку и через Ant panel plugin для FlashDevelop. Доступ к запуску тестов из командной строки удобен из соображений интеграции с сервером сборки. Ну и если вдруг Вам пригодится запустить тест на удаленной машине.
 
Последние записи от surlac

 


Часовой пояс GMT +4, время: 19:47.


Copyright © 1999-2008 Flasher.ru. All rights reserved.
Работает на vBulletin®. Copyright ©2000 - 2024, Jelsoft Enterprises Ltd. Перевод: zCarot
Администрация сайта не несёт ответственности за любую предоставленную посетителями информацию. Подробнее см. Правила.