Есть известная цитата Джеймса Греннинга, одного из пионеров методологий разработки TDD и Agile:

Если вы не занимаетесь разработкой на основе тестирования, вы занимаетесь отладкой и последующей разработкой - Джеймс Греннинг

Сегодня мы отправимся в путешествие по Laravel, основанное на тестах. Мы создадим Laravel REST API с функциями аутентификации и CRUD, не открывая Postman или браузер. 😲

Примечание. В этом пошаговом руководстве предполагается, что вы понимаете основные концепции Laravel и PHPUnit. Если у вас есть это в пути? Поехали.

Настройка проекта

Начните с создания нового проекта Laravel с composer create-project --prefer-dist laravel/laravel tdd-journey.

Затем нам нужно запустить шаблон аутентификации, который мы будем использовать, запустить php artisan make:auth, затем php artisan migrate.

Фактически мы не будем использовать сгенерированные маршруты и представления. Для этого проекта мы будем использовать jwt-auth. Так что продолжайте и настройте в своем приложении.

Примечание. Если при использовании команды generate JWT возникают ошибки, вы можете следить за этим исправлением, пока оно не будет добавлено в стабильную версию.

Наконец, вы можете удалить ExampleTest из папок tests/Unit и tests/Feature, чтобы это не повлияло на результаты наших тестов, и все готово.

Написание кода

  1. Начните с настройки конфигурации auth для использования драйвера JWT по умолчанию:

Затем добавьте в свой routes/api.php файл следующее:

2. Теперь, когда у нас настроен драйвер, настройте вашу модель пользователя таким же образом:

Мы просто реализовали JWTSubject и добавили необходимые методы.

3. Далее нам нужно добавить наши методы аутентификации в контроллер.

Запустите php artisan make:controller AuthController и добавьте следующие методы:

Этот шаг довольно прост, все, что мы делаем, это добавляем методы authenticate и register к нашему контроллеру. В методе authenticate мы проверяем ввод, пытаемся войти в систему и возвращаем токен в случае успеха. В методе регистрации мы проверяем ввод, создаем нового пользователя с вводом и на его основе генерируем токен для пользователя.

4. Затем перейдем к хорошей части. Тестируем то, что мы только что написали. Сгенерируйте тестовые классы, используя php artisan make:test AuthTest. В новом tests/Feature/AuthTest добавьте эти методы:

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

Теперь запустите $vendor/bin/phpunit или $ phpunit, если он установлен глобально. Выполнение этого должно дать вам успешные утверждения. Если это не так, вы можете просмотреть журналы, исправить и повторно протестировать. Это прекрасный цикл TDD.

5. Теперь, когда у нас работает аутентификация, давайте добавим элемент для CRUD. В этом уроке мы собираемся использовать рецепты еды в качестве элементов CRUD, потому что, почему бы и нет?

Начните с создания нашей миграции php artisan make:migration create_recipes_table и добавьте следующее:

Затем запустите миграцию. Теперь добавьте модель, используя php artisan make:model Recipe, и добавьте ее к нашей модели.

Затем добавьте этот метод в модель user.

6. Теперь нам нужны конечные точки для управления нашими рецептами. Сначала мы создадим контроллер php artisan make:controller RecipeController. Затем отредактируйте файл routes/api.php и добавьте конечную точку create.

В контроллере также добавьте метод create

Создайте тест функции с php artisan make:test RecipeTest и отредактируйте его содержимое, как показано ниже:

Код не требует пояснений. Все, что мы делаем, это создаем метод, который обрабатывает регистрацию пользователя и генерацию токена, а затем мы используем этот токен в методе testCreate(). Обратите внимание на использование признака RefreshDatabase, который представляет собой удобный способ Laravel сбрасывать вашу базу данных после каждого теста, что идеально подходит для нашего изящного небольшого проекта.

Итак, пока все, что мы хотим подтвердить, это статус ответа, продолжайте и запускайте $ vendor/bin/phpunit.

Если все пойдет хорошо, вы получите сообщение об ошибке. 😆

There was 1 failure:
1) Tests\Feature\RecipeTest::testCreate
Expected status code 200 but received 500.
Failed asserting that false is true.
/home/user/sites/tdd-journey/vendor/laravel/framework/src/Illuminate/Foundation/Testing/TestResponse.php:133
/home/user/sites/tdd-journey/tests/Feature/RecipeTest.php:49
FAILURES!
Tests: 3, Assertions: 5, Failures: 1.

Глядя на файлы журнала, мы видим, что виновником является взаимосвязь publisher и recipes в классах Recipe и User. Laravel пытается найти столбец user_id в таблице и использовать его в качестве внешнего ключа, но в нашей миграции мы установили publisher_id в качестве внешнего ключа. Теперь настройте линии, как показано ниже:

//Recipe file
public function publisher(){
    return $this->belongsTo(User::class,'publisher_id');
}
//User file
public function recipes(){
    return $this->hasMany(Recipe::class,'publisher_id');
}

А затем повторно запустите тест. Если все пойдет хорошо, мы получим все зеленые тесты! 👍

...                                                                 3 / 3 (100%)
...
OK (3 tests, 5 assertions)

Теперь нам еще нужно протестировать создание рецепта. Для этого мы можем утверждать количество рецептов пользователя. Обновите свой testCreate метод, как показано ниже:

Теперь мы можем продолжить и заполнить остальные наши методы. Время для некоторых изменений. Во-первых, наш routes/api.php

Затем мы добавляем методы в контроллер. Обновите свой RecipeController класс таким образом.

Код и комментарии уже хорошо объясняют логику.

Напоследок наш test/Feature/RecipeTest

Помимо дополнительного теста, единственным отличием было добавление пользовательского файла для всего класса. Таким образом, метод authenticate не только генерирует токен, но и устанавливает пользовательский файл для последующих операций.

Теперь запустите $ vendor/bin/phpunit, и у вас должны быть все зеленые тесты, если все сделано правильно.

Заключение

Надеюсь, это дало вам представление о том, как TDD работает в Laravel. Это определенно гораздо более широкая концепция, не привязанная к конкретному методу.

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

Полный код этого пошагового руководства доступен на Github здесь. Не стесняйтесь экспериментировать.

Ваше здоровье!