Статьи

Введение в модульное тестирование

Модульное тестирование является относительно новым для проектов ActionScript. Хотя FlexUnit существует уже некоторое время, он не был интуитивно понятным в настройке, и было много несоответствий с документацией. К счастью для нас, FlexUnit теперь встроен в Flash Builder 4. Хотя документация по-прежнему скудна, этот учебник будет хорошим учебником для настройки Test Suite, изучения нескольких примеров модульных тестов и демонстрации того, как их запускать / анализировать.

Для тех из вас, кто не знаком с модульным тестированием, давайте посмотрим, что говорит Википедия

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

В этом уроке я покажу вам, как настроить несколько простых модульных тестов на моей платформе Flash Camo. Вам нужно будет скачать копию Flash Builder 4 (в бета-версии прямо сейчас), чтобы следовать. Также вы можете скачать последнюю версию Flash Camo (2.2.1) отсюда .

Давайте создадим новый проект под названием UnitTestIntro .

create_unit_test_demo

Нам нужно поместить наш Flash Camo SWC в папку libs / swcs внутри нашего проекта:

flashcamo_setup

Наконец, нам нужно сообщить нашему проекту, где найти наш SWC, щелкнуть правой кнопкой мыши по проекту и перейти к его свойствам. Перейдите к пути сборки ActionScript и выберите вкладку «Путь к библиотеке». Нажмите Добавить папку SWC и укажите ее в каталоге lib / swcs.

add_swc_folder

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

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

Прежде чем я что-то начну, я потрачу немного времени, чтобы понять, что именно я собираюсь делать. Это очень важно при настройке юнит-тестов. Этот вид разработки называется Test Driven Development . Я думаю, что вижу другое определение:

Разработка через тестирование (TDD) — это метод разработки программного обеспечения, который использует короткие итерации разработки, основанные на предварительно написанных тестовых примерах, которые определяют желаемые улучшения или новые функции. Каждая итерация создает код, необходимый для прохождения тестов этой итерации. Наконец, программист или команда рефакторинг кода, чтобы приспособить изменения. Ключевой концепцией TDD является то, что подготовка тестов перед кодированием способствует быстрым изменениям обратной связи. википедия

Поскольку это короткое вступление, мы будем использовать существующую библиотеку кода для тестирования. Однако, когда вы создаете свое собственное приложение, вы должны писать тесты, чтобы убедиться, что код работает и что любые изменения / рефакторинг не нарушают реализацию вашего кода. Вот схема тестов, которые мы будем выполнять:

  • Создайте экземпляр CamoPropertySheet.
  • Убедитесь, что он может анализировать CSS.
  • Проверьте количество найденных селекторов.
  • Проверьте, что CamoPropertySheet можно преобразовать обратно в строку.
  • Проверьте, что происходит, когда мы запрашиваем селектор, который не был найден.
  • Подтвердите очистку CamoPropertySheet.

Если вы новичок во Flash Camo, вы можете прочитать вступление, которое я написал ( часть 1 и часть 2 ), но вы можете легко сделать это руководство без знания работы фреймворка. Опять же, это просто будет библиотека кода для тестирования.

Теперь, когда у нас есть план для нашего тестирования, давайте создадим наш первый тест. Щелкните правой кнопкой мыши по вашему проекту и выберите New> Test Case Class

new_test_case_class

Теперь вам будет представлен мастер создания. Он должен быть знаком всем, кто создал класс в Flex / Flash Builder ранее. Вот как выглядит окно:

new_testcase_class_wizard

Давайте поговорим о нескольких новых полях в мастере. Мы можем начать с того, что суперкласс уже заполнен для нас: flexunit.framework.TestCase . Вы не можете изменить это и, вероятно, не должны. Все, что это делает, это расширяет базовый тестовый класс из Структуры модульного теста. Далее вы увидите несколько флажков для генерации кода. Оставьте все это отмеченным по умолчанию. Наконец, есть поле для класса, который мы хотим протестировать.

Так как мы собираемся протестировать CamoPropertySheet Flash Camo, давайте заполните следующие значения в форме:

1
2
Name: CamoPropertySheetTest
Class to test: camo.core.property.CamoPropertySheet

Вот снимок экрана того, как я настроил это:

fill_in_new_testcase_class

Нажмите Готово, и мы должны подготовить наш первый тест, чтобы добавить к нему код.

Давайте посмотрим на код, сгенерированный для нас Flash Builder:

testcase_class

Мы начнем с того, что имеем частное свойство с именем classToTestRef, для которого установлено значение нашего CamoPropertySheet. Это позволяет тесту создать экземпляр этого класса и вынуждает компилятор импортировать его, когда мы запускаем наш тест.

Следующий важный метод — setUp . Здесь мы создадим экземпляр нашего тестового класса, настроим его и убедимся, что все готово для проведения теста.

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

Возможно, вы заметили, что в конце класса есть еще один метод testSampleMethod, который закомментирован. Это пример того, как вы можете настроить один тест.

Каждый тест будет методом, который мы добавим в этот класс. Когда мы запускаем модульный тестовый модуль, он автоматически будет динамически вызывать все наши методы, конечно, начиная с setUP и заканчивая tearDown .

Теперь, когда у нас есть общее представление о настройке TestClass, давайте посмотрим на ее запуск.

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

После того как вы откомментировали testSampleMethod , щелкните правой кнопкой мыши по нашему проекту и выберите «Выполнить как»> «Выполнить тест FlexUnit».

execute_flexunit_test

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

run_test_config

Как видите, это настроено для случаев, когда у нас есть много для тестирования, но пока у нас есть только один testSampleMethod для запуска. Нажмите «Выбрать все» и нажмите «ОК».

После запуска теста в вашем веб-браузере появится следующая страница:

swf_test_output

Если вы вернетесь в Flash Builder, вы также увидите это на панели результатов FlexUnit:

eclipse_test_output

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

Открытая веб-страница должна автоматически закрыться, но иногда этого не происходит. Это простой SWF-файл, который выполняет наши тесты во FLash и выводит некоторые данные, которые Flash Builder читает, для отображения в окончательных результатах теста. Вы можете игнорировать эту веб-страницу по большей части.

Панель «Результаты FlexUnit» — это место, где будут отображаться все результаты, а также место, где можно упорядочить и отфильтровать отзывы тестов. Мы поговорим об этом чуть позже в уроке, когда нам действительно есть что проверить.

Прежде чем мы действительно сможем приступить к тестированию, нам нужно настроить наш тестовый класс. давайте добавим следующий код в метод setUp :

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
var xml:XML = <css><![CDATA[/* This is a comment in the CSS file */
                            baseStyle {
                                x: 10px;
                                y: 10px;
                                width: 100px;
                                height: 100px;
                                padding: 5px;
                                margin: 10px;
                            }
                             
                            baseStyle .Button{
                                x: 0px;
                                y: 0px;
                                background-color: #000000;
                            }
                             
                            #playButton {
                                background-color: #FFFFFF;
                                background-image: url(‘/images/full_screen_background.jpg’);
                            }
                             
                            #fullScreenButton{
                                background-color: #FF0000;
                                background-image: url(‘/images/full_screen_background.jpg’);
                            }
                             
                            #playButton:over {
                                background-color: #333333;
                            }
                             
                            interactive {
                                cursor: hand;
                            }
                                    ]]>
                                        </css>;
rawCSS = xml.toString();
sheet = new CamoPropertySheet();
sheet.parseCSS(rawCSS);

Нам также нужно будет настроить некоторые свойства:

1
2
private var sheet:CamoPropertySheet;
private var rawCSS:String;

Вот что происходит в этой настройке: чтобы протестировать наш CamoPropertySheet, нам понадобится CSS в виде строки. Обычно я загружаю CSS из внешнего файла, но так как мы делаем простой тест, я просто создаю новый блок XML и помещаю текст CSS внутри первого узла. Обычно вам не нужно оборачивать CSS для CamoPropertySheet внутри XML, но при работе с большими строками внутри редактора мне проще использовать xml, так как вы можете переносить текст и он сохраняет некоторое форматирование.

Далее вы увидите, что мы установили для нашего свойства rawCSS строковое значение xml. Это преобразует XML в строку. Затем мы создаем новый CamoPropertySheet. Наконец, мы говорим листу для анализа rawCSS.

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

Давайте получим право на это. Как только CamoPropertySheet успешно проанализировал строку css, мы можем запросить массив имен селекторов, чтобы убедиться, что все действительно было проанализировано. Для тех, кто не знаком с CSS-жаргоном, селектор — это имя стиля CSS, т.е. baseStyle {…} будет иметь селектор с именем baseStyle .

Вот как будет выглядеть наш тест на английском языке:

  1. Получить список селекторов из CamoPropertySheet.
  2. Получить длину массива селектора.
  3. Сравните значение длины с 6 (число, которое мы ожидаем, вернулось).

Давайте заменим наш testSampleMethod следующим методом:

1
2
3
4
5
6
7
public function testParseCSS():void
{
    var selectors:Array = sheet.selectorNames;
    var total:Number = selectors.length;
     
    assertEquals(total,6);
}

Как видите, мы получаем массив имен селекторов. Затем мы получаем итоговую сумму и представляем наш первый тест assetEquals . На следующем шаге я объясню assertMethods более подробно, но давайте просто запустим это и посмотрим, пройдет ли тест.

При запуске теста вы должны увидеть следующее на панели результатов FlexUnit:

first_test_pass

Хорошо, наше испытание прошло. Мы получили точное количество селекторов, которые мы ожидали. Давайте посмотрим, какие тесты утверждений мы можем использовать.

В модульном тестировании мы запускаем утверждения. Каждое утверждение обрабатывает определенный тип теста. Вот краткий обзор наиболее распространенных утверждений, которые вы, вероятно, будете использовать:

  • assertEquals — проверить, равно ли одно значение другому.
  • assertFalse — проверить, равно ли значение false.
  • assertNotNull — проверить, не равно ли значение нулю.
  • assertNotUndefined — проверить, не является ли значение неопределенным.
  • assertNull — проверить, если значение равно нулю.
  • assertStrictlyEquals — проверяет, строго ли совпадают два значения.
  • assertTrue — проверить, является ли значение истинным.
  • assertUndefined — проверить, является ли значение неопределенным.

Теперь, прежде чем мы протестируем пример каждого из них, давайте настроим наш метод tearDown .

Это будет очень короткий шаг, но очень важный. Давайте добавим следующую строку в наш метод tearDown после super.tearDown () :

1
sheet = null;

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

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

Мы уже видели пример этого в шаге 7, но давайте пройдемся и добавим еще один assertEquals . Вот следующий тест, который мы выполним:

  1. Сжатие текста CSS (удалите пробелы, специальные символы и другие препятствия, которые синтаксический анализатор может не распознать), поскольку CamoPropertySheet автоматически сжимает тест CSS, когда он анализируется.
  2. Преобразуйте CamoPropertySheet в текст (это будет сжатая версия rawCSS, которую мы использовали ранее).
  3. Сравните, что текст CamoPropertySheet равен нашей сжатой строке CSS.

Для запуска теста добавим следующий метод:

1
2
3
4
5
public function testToString():void
{
    var compressedCSS:String = «baseStyle{x:10;y:10;width:100;height:100;padding:5;margin:10;}baseStyle .Button{x:0;y:0;background-color:#000000;}#playButton{background-color:#FFFFFF;background-image:url(‘/images/full_screen_background.jpg’);}#fullScreenButton{background-color:#FF0000;background-image:url(‘/images/full_screen_background.jpg’);}#playButton:over{background-color:#333333;}interactive{cursor:hand;}»;
    assertEquals(sheet.toString(), compressedCSS);
}

Теперь запустите Unit Test и убедитесь, что вы выбрали оба теста из флажков. Новые тесты не выбираются автоматически. Если все прошло хорошо, вы должны увидеть успех и выполнить 2 теста.

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

  1. Сделайте запрос на селектор, который не существует.
  2. Проверьте возвращенное имя селектора, чтобы увидеть, равно ли оно «EmptySelector» — константа класса PropertySelector.
  3. Утвердите, если значение ложно.

Вот код для выполнения теста:

1
2
3
4
5
6
7
public function testEmptySelector():void
{
    var selector:PropertySelector = sheet.getSelector(«testSelector»);
    var exists:Boolean = (selector.selectorName == PropertySelector.DEFAULT_SELECTOR_NAME) ?
     
    assertFalse(exists);
}

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

Далее мы хотим убедиться, что текстовое значение из нашего CamoPropertySheet никогда не равно нулю. Давайте посмотрим, как структурировать наш тест:

  1. вызовите toString в нашем экземпляре CamoPropertySheets и проверьте, не равен ли он нулю

Вот наш метод испытаний:

1
2
3
4
public function testCSSValue():void
{
    assertNotNull(sheet.toString());
}

Это довольно просто, поэтому при запуске теста у нас должно получиться 5 успехов. Каждый раз, когда мы запускаем тест, мы можем проверять имена тестов наших методов, щелкая папку Default Suite на нашей панели результатов FlexUnit обратно во Flash Builder.

тестовый забег

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

  1. Получить селектор, который не существует.
  2. Получить селектор, который существует.
  3. Проверьте, не определены ли имена обоих селекторов.

Вот метод испытания:

1
2
3
4
5
6
7
public function testSelectorsHaveNames():void
{
    var selectorA:String = sheet.getSelector(«testSelector»).selectorName;
    var selectorB:String = sheet.getSelector(«baseStyle»).selectorName;
     
    assertNotUndefined(selectorA, selectorB);
}

Первые две строки говорят сами за себя; мы просто запрашиваем два селектора, и один из которых, как мы знаем, не существует. Однако, когда мы сделаем утверждение, вы заметите, что мы передаем два значения вместо обычного, которое мы делали до этого момента. Это не уникальный пример, фактически каждый из методов assert позволяет передавать любое количество значений для тестирования. Здесь мы просто убедитесь, что selectorA и selectorB не определены.

Вот пример того, как строго сравнивать два объекта. Здесь я использую строки, которые могут быть не лучшим использованием этого примера, но приятно видеть тест в действии. Что мы будем делать?

  1. Клонируйте CamoPropertySheet.
  2. Проверьте, что строковое значение нашего CamoPropertySheet равно значению клонированного CamoPropertySheet.
1
2
3
4
5
6
public function testClone():void
{
    var clone:CamoPropertySheet = sheet.clone() as CamoPropertySheet;
     
    assertStrictlyEquals(sheet.toString(), clone.toString());
}

Как вы можете видеть, мы вызываем метод clone CamoPropertySheet, чтобы получить точную копию PropertySheet. Затем мы запускаем его через тест assert, вызывая метод toString для каждого из них. Если возвращенный CSS-тест совпадает, у нас есть успех для теста.

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

  1. Запросите селектор baseStyle .
  2. Проверьте, имеет ли селектор свойство x .

Вот метод испытания:

1
2
3
4
5
public function testSelectorHasProperty():void
{
    var selector:PropertySelector = sheet.getSelector(«baseStyle»);
    assertTrue(selector.hasOwnProperty(«x»));
}

Как вы можете видеть здесь, мы ожидаем, что наш селектор baseStyle будет иметь свойство x. Если это существует, мы можем предположить, что он был правильно проанализирован из строки CSS. Поскольку он существует, мы прошли этот тест.

Каждый из этих тестов становится понятным о том, как вы их реализуете. Давайте посмотрим, что произойдет, если мы провалим тест в следующих двух шагах.

Мы собираемся проверить неопределенность сейчас, но Flash Camo был разработан, чтобы не возвращать неопределенное. Так что следующий тест не пройдёт. Давайте проверим, что мы собираемся проверить.

  1. Вызовите метод clear в CamoPropertySheet.
  2. Проверьте, не вызовет ли toString значение undefined.

Вот код для теста:

1
2
3
4
5
public function testClear():void
{
    sheet.clear();
    assertUndefined(sheet.toString());
}

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

Если вы выполнили предыдущий шаг и запустили модульное тестирование, вы должны увидеть следующее на панели «Результаты FlexUnit»:

Тест не пройден

Обратите внимание, что у нас есть 1 сбой из нашего метода testClear ?

Если дважды щелкнуть неудачный тест на панели «Результаты теста», вы перейдете к источнику неудачного теста. Это отличный способ исправить вашу ошибку или изменить тест, чтобы он не провалился. Не намного больше провала теста, чем этот. Каждый неудачный тест будет отображаться на этой панели. Вы можете указать панели показывать только неудачные тесты, нажав на красный восклицательный знак выше, где указано, сколько ошибок у вас было.

Теперь, когда мы провалили этот тест, замените его следующим:

1
2
3
4
5
public function testClear():void
{
    sheet.clear();
    assertEquals(sheet.toString(),»»);
}

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

До этого момента мы тестировали предварительно скомпилированную библиотеку, но вам может быть интересно, как это будет работать на ваших собственных классах. Мы собираемся немного изменить класс doc, а затем запустить собственный модульный тест. Для начала замените весь код в классе UnitTestIntro следующим:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package
{
    import flash.display.Sprite;
     
    public class UnitTestIntro extends Sprite
    {
         
        private var _firstName:String;
        private var _lastName:String;
        private var _loggedIn:Boolean;
         
        public function UnitTestIntro()
        {
            _firstName = «Jesse»;
            _lastName = «Freeman»;
        }
         
        public function get firstName():String
        {
            return _firstName;
        }
         
        public function get lastName():String
        {
            return _lastName;
        }
         
        public function isLoggedIn():Boolean
        {
            return _loggedIn;
        }
    }
}

Когда код будет готов, щелкните правой кнопкой мыши UnitTestIntro и выберите «Создать»> «Класс теста». Если вы посмотрите на мастера в этот раз, вы увидите, что все поля заполнены для нас:

unit_test_intro_test

На этот раз вместо нажатия «Готово» нажмите «Далее» и посмотрите на следующее окно:

unit_test_automation

Здесь вы можете выбрать все открытые методы этого класса для тестирования. Обратите внимание, что наши методы get для firstName и lastName не являются частью этого списка. Модульное тестирование может проводиться только публичными методами. Кроме того, вы увидите все унаследованные методы класса, поэтому у нас есть методы Sprite / DisplayObject, поскольку наш класс документов расширяет Sprite. Выберите isLoggedIn и нажмите Готово .

Если вы прокрутите вниз до конца только что созданного тестового класса, вы увидите, что он автоматически добавлен в testMethod для isLoggedIn.

test_created_automatically

При тестировании собственного кода Flash Builder может помочь автоматизировать процесс создания лесов для ваших тестов. Это очень помогает при работе с большими классами, в которых много методов.

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

  • Держите ваш код маленьким и легко тестируемым. Делайте короткие методы, разбивайте код на «единицы», чтобы упростить тестирование. В ваших классах нормально иметь много методов. Это поможет вам не только при модульном тестировании, но и при расширении классов и работе с переопределением наследования.
  • Проверьте поведение, а не метод. Этот урок показал вам, как я буду взаимодействовать с экземпляром CamoPropertySheet. Я тестировал поведенческие ответы для базовой системы синтаксического анализа / поиска. Убедитесь, что вы не проверяете, что функции просто возвращают значения, но основная логика верна. Что-то было проанализировано, зависимые методы сделали то, что от них ожидали?
  • Держите ваши тестовые имена в чистоте. Вы должны быть в состоянии легко понять, что происходит, просто взглянув на название метода тестирования. Помните, что этот код не скомпилирован в ваше окончательное приложение, поэтому, если у вас невероятно длинные имена методов, это нормально, если они описательны.
  • Не полагайтесь на окно консоли для вашего теста. Это означает, что вы не должны ожидать, что разработчик будет наблюдать результаты трассировки, чтобы увидеть, работает ли тест правильно. Вместо этого сделайте тест неудачным или успешным и не выводите его результаты.
  • Выполните поиск модульного тестирования на других языках, чтобы увидеть, как оно было реализовано в других местах. Также возьмите книгу по разработке через тестирование (TDD).

Как видите, настройка Unit Testing очень проста, но создание приложений, основанных на Test Driven Development, само по себе является искусством. Надеюсь, после этого вступления вам будет удобно настроить простой тест для проверки того, что ваш код работает должным образом. По мере того, как вы будете все больше полагаться на модульное тестирование, количество ошибок в вашем коде будет значительно уменьшаться. Пока вы не забываете писать код для прохождения теста, сохраняя свои методы небольшими и часто проверяя свои модульные тесты, вы будете на пути к созданию более стабильного кода.

Спасибо за прочтение.