Традиционная разработка, основанная на тестировании, может порой быть громоздкой. Вы должны прекратить писать код для запуска ваших тестов. К счастью, есть решения, которые предоставляют возможность автоматического запуска ваших тестов при кодировании. В этом руководстве вы узнаете, как использовать гем Ruby, называемый watchr , для мониторинга вашего кода и автоматического запуска соответствующих тестов всякий раз, когда вы сохраняете свою работу.
Шаг 1: Требования к программному обеспечению
Любой инструмент, который поможет вам получить более быструю обратную связь, является ценным активом.
В этом руководстве для примера кода используется PHP, однако методы применимы для любого языка, который предлагает утилиту CLI для модульного тестирования. Требуется Ruby, потому что мы будем использовать гем watchr. Итак, убедитесь, что у вас есть работающая установка Ruby и PHP с PHPUnit .
Затем убедитесь, что у вас установлен libnotify , если вы работаете в Linux; Пользователям Windows и Mac OSX требуется «Growl». Этот учебник применим непосредственно в Linux, но я буду предлагать альтернативные команды и настройки, где это возможно.
Теперь пришло время установить сторожевой механизм. Откройте консоль и убедитесь, что вы находитесь в папке, где вы можете напрямую запустить gem
. Введите следующую команду:
1
|
gem install watchr
|
Шаг 2: Техническая информация
Когда файл или папка изменены, наблюдатель может вызвать функцию обратного вызова.
Watchr gem — это исполняемая программа, написанная на Ruby, и она охватывает функции, обнаруженные в файловой системе операционной системы, чтобы обеспечить возможность отслеживания изменений, внесенных в конкретный файл или папку. Естественно, эти функции файловой системы различаются для каждой операционной системы и файловой системы.
watchr предоставляет единый интерфейс прикладного программирования (API) для всех операционных систем. В Linux он использует inotify , библиотеку событий файловой системы ядра; в других операционных системах используется соответствующая альтернатива. Если по какой-либо причине в операционной системе нет доступной службы событий, средство наблюдения периодически опрашивает просматриваемый файл или папку.
Когда файл или папка изменены, наблюдатель может вызвать функцию обратного вызова. Мы будем использовать эту функцию для запуска наших тестов.
Шаг 3: Создайте проект PHP
Наш проект довольно прост. Скопируйте простую структуру каталогов, показанную на следующем рисунке:
В файле Nettuts.php
добавьте следующий код:
1
2
3
4
5
6
7
|
<?php
class Nettuts {
}
?>
|
Затем добавьте следующий код в NettutsTest.php
:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
|
<?php
require_once dirname(__FILE__) .
class NettutsTest extends PHPUnit_Framework_TestCase {
protected $object;
protected function setUp() {
$this->object = new Nettuts;
}
protected function tearDown() {
}
}
?>
|
На данный момент тестовый файл — это просто скелет, и, как вы можете видеть на рисунке выше, тесты проходят.
Шаг 4: Создайте первый скрипт наблюдателя
Теперь нам нужно создать файл Ruby в папке нашего проекта; давайте назовем это autotest_watchr.rb
. Затем добавьте следующий код в файл:
1
2
3
|
watch(«Classes/(.*).php») do |match|
run_test %{Tests/#{match[1]}Test.php}
end
|
Автоматизированные тесты независимы от IDE — большой плюс в моей книге.
Этот код использует метод watch
для просмотра всех файлов .php
в папке Classes
нашего проекта. При изменении файла .php
операционная система выдает событие, и наш метод watch
будет запущен. Имя файла .php
возвращается (за вычетом расширения) в позиции массива соответствия 1
. Как и с любым регулярным выражением, круглые скобки используются для указания переменной соответствия, и в этом коде мы используем их в условии соответствия для получения имени файла. Затем мы вызываем метод run_test
с путем к имени run_test
тестового файла.
Мы также должны посмотреть наши тестовые файлы; Итак, добавьте следующий код в файл Ruby:
1
2
3
|
watch(«Tests/.*Test.php») do |match|
run_test match[0]
end
|
Обратите внимание, что массив match
содержит полное имя файла в позиции 0
, и мы передаем его непосредственно методу run_test
.
Шаг 5. Сделайте скрипт запускающим тесты
Сценарий Ruby настроен для просмотра наших файлов .php
, и теперь нам нужно реализовать метод run_test
. В нашем случае мы хотим запустить PHPUnit для конкретного файла.
01
02
03
04
05
06
07
08
09
10
|
def run_test(file)
unless File.exist?(file)
puts «#{file} does not exist»
return
end
puts «Running #{file}»
result = `phpunit #{file}`
puts result
end
|
Сначала мы гарантируем, что файл существует, и просто возвращаем его, если его нет. Далее мы запускаем тест с PHPUnit и отправляем результат на консоль. Давайте запустим наш скрипт наблюдения. Откройте консоль, перейдите в каталог вашего проекта и выполните:
1
|
watchr ./autotest_watchr.rb
|
Пользователи Windows должны опустить «./» в приведенной выше команде.
Теперь измените один из файлов .php
(просто добавьте пустую строку в конце файла), сохраните его и просмотрите вывод в консоли. Вы должны увидеть нечто похожее на то, что показано ниже:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
|
Running Tests/NettutsTest.php
PHPUnit 3.6.0 by Sebastian Bergmann.
F
Time: 0 seconds, Memory: 3.75Mb
There was 1 failure:
1) Warning
No tests found in class «NettutsTest».
/usr/bin/phpunit:46
FAILURES!
Tests: 1, Assertions: 0, Failures: 1.
|
Да, у нас еще нет теста для запуска; так что давайте введем фиктивный тест. Добавьте следующий код в тестовый файл PHP:
1
2
3
|
function testDummyPassingTest() {
$this->assertTrue(true);
}
|
Запустите скрипт Ruby еще раз, и вы должны увидеть:
1
2
3
4
5
6
7
8
|
Running Tests/NettutsTest.php
PHPUnit 3.6.0 by Sebastian Bergmann.
.
Time: 0 seconds, Memory: 3.75Mb
OK (1 test, 1 assertion)
|
Шаг 6: Разбор результатов теста
Давайте уведомим пользователя через механизм уведомления системы о результатах теста. Мы run_tests
метод run_tests
чтобы вызвать метод, называемый notify
. Ниже приведен модифицированный run_tests
:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
|
def run_tests(file)
unless File.exist?(file)
puts «#{file} does not exist»
return
end
puts «Running #{file}»
result = `phpunit #{file}`
puts result
if result.match(/OK/)
notify «#{file}», «Tests Passed Successfuly», «success.png», 2000
end
end
|
Имя файла изображения, success.png
, указывает на изображение, которое вы хотите отобразить в области уведомлений. Это изображение не предоставляется в этом руководстве; так что вам нужно будет найти свой собственный. Теперь давайте напишем метод notify
:
1
2
3
4
|
def notify title, msg, img, show_time
images_dir=’~/.autotest/images’
system «notify-send ‘#{title}’ ‘#{msg}’ -i #{images_dir}/#{img} -t #{show_time}»
end
|
Пользователи Mac OSX и Windows: замените команду notify-send
соответствующей альтернативой Growl. Измените что-либо в своем тестовом файле или файле кода, чтобы тест все еще проходил. Сохраните измененный файл PHP и наблюдайте, как происходит волшебство. Ниже приведено изображение результата в моей системе:
Далее нам нужно ловить сбои. Следующий код добавляет пару строк в run_tests
:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
|
def run_tests(file)
unless File.exist?(file)
puts «#{file} does not exist»
return
end
puts «Running #{file}»
result = `phpunit #{file}`
puts result
if result.match(/OK/)
notify «#{file}», «Tests Passed Successfuly», «success.png», 2000
elsif result.match(/FAILURES\!/)
notify_failed file, result
end
end
|
Кроме того, давайте добавим метод notify_failed
в файл:
1
2
3
4
|
def notify_failed cmd, result
failed_examples = result.scan(/failure:\n\n(.*)\n/)
notify «#{cmd}», failed_examples[0], «failure.png», 6000
end
|
Измените любой из ваших файлов PHP, чтобы сделать тест неудачным; сохраните измененный файл. Соблюдайте уведомление. Он содержит имя первого провального теста. Это имя выбирается регулярным выражением в методе notify_failed
, который анализирует выходные данные PHPUnit.
Шаг 7: очищайте консоль перед каждым тестовым запуском
Добавьте следующий метод в ваш скрипт на Ruby и обязательно вызовите его в методе run_test
. Код должен работать в Linux и Mac OSX, хотя вам может потребоваться провести некоторые исследования для Windows.
1
2
3
|
def clear_console
puts «\e[H\e[2J» #clear console
end
|
Вывод
Всякий раз, когда вы программируете с использованием TDD, любой инструмент, который помогает вам получить более быструю обратную связь, является ценным активом. Мои коллеги используют похожие скрипты с наблюдателем или альтернативами (некоторые написаны вокруг fs_event
на MacOS). Излишне говорить, что мы сейчас избалованы и не можем представить, что будем разрабатывать что-либо без автоматического запуска тестов.
Автоматизированные тесты независимы от IDE — большой плюс в моей книге. Слишком много IDE вынуждают вас использовать определенную среду тестирования, и я не начинаю удаленное тестирование. Я предпочитаю ежедневно использовать подобные сценарии и обязательно рекомендую их любому проворному разработчику программного обеспечения.