Как писать модульные тесты для интерактивного консольного приложения

у меня есть консольное приложение (написано как команда Symfony2), которое считывает ввод от пользователя через STDIN и с помощью readline, ввод пользователя затем передается в eval()

все это просто для того, чтобы иметь "debug shell" (что-то вроде php -a), но в рамках проекта env и доступа к контейнеру инъекций зависимостей.

Я хотел бы написать модульные тесты для этой команды, но я ударяю по стене, о том, как (и возможно ли) написать тесты PHPUnit для этого поведение?

1 ответов


Я не знаком с командой Sf2, но в документах Sf2 есть пример ее тестирования в http://symfony.com/doc/2.0/components/console.html#testing-commands

в общем, вы можете отделить STDIN и STDOUT из консольного приложения, чтобы вы могли заменить его другим потоковым ресурсом, например fopen(php://memory). Вместо readline, вы используете

fwrite($outputStream, 'Prompt');
$line = stream_get_line($inputStream, 1024, PHP_EOL);

идея состоит в том, чтобы сделать ваш компонент тестируемым, не требуя реальной консоли окружающая среда. Использование этого подхода позволяет проверить содержимое потока в любое время в тесте. поэтому, если вы запустите команду " foo "в своем консольном приложении и хотите проверить, что вывод" bar", вы просто перемотаете соответствующий ресурс и прочитаете его содержимое. Альтернативой было бы использовать SplTempFileObject.

class ConsoleApp
…
    public function __construct($inputStream, $outputStream)
    {
        $this->inputStream = $inputStream;
        $this->outputStream = $outputStream;
    }
}

в вашем сценарии реального мира вы создадите консольное приложение с

$app = new ConsoleApp(STDIN, STDOUT);

но в вашем тесте вы можете установить ConsoleApp потоком на ваш выбор:

public function setup()
{
    $i = fopen('php://memory', 'w');
    $o = fopen('php://memory', 'w');
    $this->consoleApp = new ConsoleApp($i, $o);
}

примером UnitTest, использующего этот метод для outstream, будет