Традиционная разработка, основанная на тестировании, может порой быть громоздкой. Вы должны прекратить писать код для запуска ваших тестов. К счастью, есть решения, которые предоставляют возможность автоматического запуска ваших тестов при кодировании. В этом руководстве вы узнаете, как использовать гем 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 вынуждают вас использовать определенную среду тестирования, и я не начинаю удаленное тестирование. Я предпочитаю ежедневно использовать подобные сценарии и обязательно рекомендую их любому проворному разработчику программного обеспечения.