Ошибка тестирования с Laravel Prompts в Laravel-Zero

от автора

Laravel Zero — это микро-фреймворк для консольных приложений на базе Laravel Framework, позволяющий легко и быстро разрабатывать функционал работающий без веба. Но инициатором этой статьи стал кейс и несколько часов потраченного времени.

Вводная

При разработке интерактивного консольного приложения на базе Laravel Zero, я решил использовать Laravel Prompts в качестве гибкого, лёгкого и красивого взаимодействия с пользователем. И, пока разработка велась под Windows, проблем не было никаких и внезапное осознание того, что под Windows всё работает, а под Linux — нет, от части привело меня в ступор…

Тестирование

Итак, всё по порядку. Допустим, в приложении имеется консольная команда с таким кодом:

use Illuminate\Console\Command;  use function Laravel\Prompts\text;  class FooCommand extends Command {     protected $signature = 'foo';      protected $description = 'Command description';      public function handle(): void     {         $first  = text('First question');         $second = text('Second question');          $this->info('First is ' . $first);         $this->info('Second is ' . $second);     } }

Также имеем Pest тест:

use App\Commands\FooCommand;  it('some', fn() => test()->artisan(FooCommand::class)     ->expectsQuestion('First question', 'qwerty')     ->expectsQuestion('Second question', 'wasd')     ->expectsOutputToContain('First is qwerty')     ->expectsOutputToContain('Second is wasd')     ->assertSuccessful() );

При запуске теста под Windows проблем с ним никаких не будет:

$ vendor/bin/pest --filter FooTest     PASS  Tests\Feature\FooTest    ✓ it some                                                                                                                                                                                                                                                                                                                                                                                                                                                                           0.34s      Tests:    1 passed (10 assertions)   Duration: 0.51s 

НО стоит запустить его в WSL (Ubuntu), линуксе или любом месте, что не является Windows, то сразу получаем ошибку:

$ vendor/bin/pest --filter FooTest   FAILED  Tests\Feature\FooTest > it some AssertionFailedError     Question "First question" was not asked.    at vendor/illuminate/testing/PendingCommand.php:370     366▕      */     367▕     protected function verifyExpectations()     368▕     {     369▕         if (count($this->test->expectedQuestions)) {   ➜ 370▕             $this->test->fail('Question "'.Arr::first($this->test->expectedQuestions)[0].'" was not asked.');     371▕         }     372▕     373▕         if (count($this->test->expectedChoices) > 0) {     374▕             foreach ($this->test->expectedChoices as $question => $answers) {        +3 vendor frames    4   tests/Feature/FooTest.php:13

Это значит, что консоль либо не может, либо даже не пытается ввести данные.

Исследование

На странице документации Prompts есть раздел где сказано, что из-за особенностей Windows, а также некоторых окружений, нужно явно использовать fallback значения.

Начиная с Laravel Framework 10.17, библиотека Laravel Prompts поставляется «в коробке», а значит явно должна этой «коробкой» использоваться. И мы не ошиблись! В коде консольных команд присутствует трейт ConfiguresPrompts с реализацией нужных fallback значений. Это нам на руку, так как не придётся изобретать велосипед.

Но вопрос в ошибке тестирования остаётся открытым. Изучая код, на четвёртой же его строчке видим функцию активации этих fallback значений:

Prompt::fallbackWhen(windows_os() || $this->laravel->runningUnitTests());

И становится понятно почему под Windows всё работает, а в остальных случаях нет. Вызов dd($this->laravel->runningUnitTests()) возвращающий false подтверждает догадки.

А ларчик просто открывался…

Метод runningUnitTests достаточно прост и он проверяет имя окружения под которым запущена команда:

public function runningUnitTests() {     return $this->bound('env') && $this['env'] === 'testing'; }

Добавив над оператором возврата строчку dd($this['env']) внезапно получаем development. И он выглядит очень знакомым… 😑

Где в Laravel приложениях задаётся имя окружения при тестировании? Правильно! в phpunit.xml файле. Открываем файл и что видим? А ничего мы не видим! Окружение-то не задаётся вовсе так как оно нам не нужно. Куда нужно идти в этом случае? Верно! В config/app.php и видим…

return [     // ...     'env' => 'development',     // ... ];

🤦‍♂️🤦‍♂️🤦‍♂️

Решение

Теперь настройки окружения нам нужны, но не полностью. Таким образом, в файл phpunit.xml добавляем строчки:

<php>     <env name="APP_ENV" value="testing" /> </php>

Затем в знакомом файле config/app.php меняем значение на:

-'env' => 'development', +'env' => env('APP_ENV', 'production'),

Запускаем тесты и наслаждаемся результатом:

$ vendor/bin/pest --parallel    .................................    Tests:    33 passed (108 assertions)   Duration: 2.69s   Parallel: 16 processes 

Всё.


ссылка на оригинал статьи https://habr.com/ru/articles/892010/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *