Пресловутый TDD
Запись от surlac размещена 24.01.2011 в 20:19
Обновил(-а) surlac 27.01.2011 в 13:24 (global refactoring)
Обновил(-а) 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
- реализуем небольшой примерчик
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
Сам тест-кейс. Хранит методы для тестирования. Рекомендуется наследовать от TestCase, т.к. он агрегирует класс Assert и оборачивает его методы.
В качестве примера, протестируем обработчик абсолютно упругого столкновения RecoilProcessor для шаров произвольного радиуса. Создадим искусственную ситуацию, когда шарик движется по оси OX с заданной скоростью и сталкивается с вертикальной стеной.
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.
package ru.pkg { import ru.pkg.core.RecoilProcessorTest; [Suite] public class AllTests { public var _RecoilProcessorTest:RecoilProcessorTest; } }
Главный класс для запуска Suite. Должен быть наследником DisplayObject, т.к. выводит результаты на экран. TextCore настраивает объектами классов Suite и TextPrinter ядро asunit - AsUnitCore.
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-репозитории.
Всего комментариев 10
Комментарии
|
|
|
Спасибо! Занятно будет почитать) а то что то эта аббревиатура начала все чаще мелькать перед глазами.
|
|
|
|
Бессистемная статья. Расскажите, пожалуйста, зачем заворачивать UnitTest в ant? Вы даже толком (ссылками) не объяснили про этот инструмент. Зачем даете описание опций компилятора? Где объяснение непоняток? Что такое "функционал", наконец?
Нужно работать. |
|
|
|
Вот мне, как человеку не знающему зачем TDD нужен, после такой статьи ещё меньше захотелось знать об этом. Меня пугает, то, что для сравнения пяти с пятью нужно столько знать.
|
|
|
|
Во флеше TDD не нужен. Слишком низкие стандарты качества.
|
|
|
|
Странное заявление. Как будто технология задаёт стандарты качества? Скорее во флеше TDD мало кто используют ибо не умеют и не удобно для конкретных проектов.
По большому счёту я хоть и пробовал использовать Unit тесты, мне они не помогали, а только мешали ибо моя область деятельности клиент/сервер и соответственно основные ошибки трудно отлавливаются на момент компиляции. Тут в дело вступает логирование и багрепорты. В качестве юниттестеров выступают люди, автоматизация такого тестирования может быть дороже и сложнее самого тестируемого контента. А насчёт статьи, хотелось бы больше информации об ант, как ставить, как работать в FB/FD/Idea, как писать свои сценарии, синтаксис, настройка и т.п. |
|
|
Обновил(-а) Котяра 25.01.2011 в 17:53
|
|
|
|
Цитата:
хотелось бы больше информации об ант, как ставить, как работать в FB/FD/Idea, как писать свои сценарии, синтаксис, настройка и т.п.
|
|
|
|
Благодарю за толковые комментарии, особенно dimarik.
Цитата:
Сообщение от Котяра
А насчёт статьи, хотелось бы больше информации об ант, как ставить, как работать в FB/FD/Idea, как писать свои сценарии, синтаксис, настройка и т.п.
. Только в другой статье. В этой статье я старался делать упор на практической реализации TDD во Flash, и ANT тут используется только для удобства (разделение на test и src) и возможности дальше капнуть в сторону непрерывной интеграции. Посмотрите тут, отличная дока.Цитата:
Сообщение от Rzer
Вот мне, как человеку не знающему зачем TDD нужен, после такой статьи ещё меньше захотелось знать об этом. Меня пугает, то, что для сравнения пяти с пятью нужно столько знать.
Другое дело, что само управление проектами и TDD в частности имеет максимум эффективности при достаточно больших командах (3 до 12 чел - см. "Scrum and XP from the Trenches") и при циклах разработки от 2 до 6 недель (т.е достаточно большой проект). Согласен с Вами в том, что если проект небольшой - максимума эффективности от TDD мы не добьемся. Цитата:
Сообщение от †‡Paladin‡†
Во флеше TDD не нужен. Слишком низкие стандарты качества.
|
|
|
|
Цитата:
Как будто технология задаёт стандарты качества?
Цитата:
Нужен/не нужен определяется индивидуально для каждого проекта. Само собой Вы можете напрочь отказаться от инструментов, позволяющих успешно завершать проекты и делать всё по-своему. Без проблем. И это работает, но только для небольших проектов и команд.
|
|
|
Обновил(-а) †‡Paladin‡† 26.01.2011 в 16:46
|
|
|
|
Цитата:
Нужен/не нужен определяется индивидуально для каждого проекта. Само собой Вы можете напрочь отказаться от инструментов, позволяющих успешно завершать проекты и делать всё по-своему. Без проблем. И это работает, но только для небольших проектов и команд.
Цитата:
Вот мне, как человеку не знающему зачем TDD нужен, после такой статьи ещё меньше захотелось знать об этом. Меня пугает, то, что для сравнения пяти с пятью нужно столько знать.
Автору: От теста "assertEquals(5, 5)", даже не "assert(2 + 2, 4)", сразу переходить к непрерывной интеграции?? Не слишком ли круто? Может лучше рассмотреть создание конкретного класса с помощью тестов? Оффтоп: surlac, а как вы ant-файлы запускаете, через коммандную строку или в IDE горячую клавишу настроили? |
|
|
Обновил(-а) expl 26.01.2011 в 23:52
|
|
|
|
Цитата:
Сообщение от expl
От теста "assertEquals(5, 5)", даже не "assert(2 + 2, 4)", сразу переходить к непрерывной интеграции?? Не слишком ли круто?
. Сложнее всего начать пользоваться чем-то новым. Эта статья как раз направлена на то, чтобы описать как начать пользоваться TDD для Flash. Нужно оно Вам или нет решать опять же Вам.Цитата:
Сообщение от expl
Может лучше рассмотреть создание конкретного класса с помощью тестов?
Цитата:
Сообщение от expl
Оффтоп:
surlac, а как вы ant-файлы запускаете, через коммандную строку или в IDE горячую клавишу настроили? |
Последние записи от surlac
- Пресловутый TDD (24.01.2011)












