Рубрики
Uncategorized

Создание API Laravel REST с помощью разработки на основе тестов TDD

Автор оригинала: David Wong.

У Джеймса Греннинга, одного из пионеров TDD и гибкой разработки, есть знаменитое высказывание:

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

Сегодня мы отправляемся в тур по разработке на основе тестов Laravel. Мы создадим полный API Laravel REST с функциями аутентификации и CRUD, не открывая почтальона или браузер. ?

Примечание: Эта поездка предполагает, что вы поняли основные концепции Laravel и PHPUnit. Вы ясно дали это понять? Давайте начнем.

Параметры проекта

Сначала создайте новый проект Laravel composer create-project --prefer-dist laravel /laravel tdd-путешествие

Затем нам нужно создать каркас аутентификации пользователя для выполнения. php artisan make:auth Настройка .env После настройки базы данных в файле выполняется миграция базы данных. php ремесленник мигрирует

В этом тестовом проекте не будут использоваться вновь созданные маршруты и представления, связанные с аутентификацией пользователей. Мы будем использовать jwt-auth. Поэтому вам нужно продолжить установку JWT в проект.

Примечание: Если вы выполняете jwt:создайте Вы можете обратиться к ошибке, обнаруженной в инструкции, чтобы решить эту проблему, пока JWT не будет правильно установлен в проекте.

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

Код

  1. Сначала настройте драйвер JWT auth Значения по умолчанию для элементов конфигурации:
 [
    'guard' => 'api',
    'passwords' => 'users',
],
'guards' => [
    ...
    'api' => [
        'driver' => 'jwt',
        'provider' => 'users',
    ],
],

Затем поместите следующее в свой routes/api.php В документе:

 'api', 'prefix' => 'auth'], function () {
    Route::post('authenticate', '[email protected]')->name('api.authenticate');
    Route::post('register', '[email protected]')->name('api.register');
});
  1. Теперь мы завершили настройки драйвера, как и делали, чтобы настроить вашу пользовательскую модель:
getKey();
    }
    // Returns an array of key-value pairs containing any custom claim to be added to JWT
    public function getJWTCustomClaims()
    {
        return [];
    }
}

Что нам нужно сделать, так это добиться этого. Тема JWT Затем интерфейс добавляет соответствующий метод.

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

Функция php ремесленник делает:контроллер AuthController И добавьте следующие методы:

validate($request,['email' => 'required|email','password'=> 'required']);
        // Test Verification
        $credentials = $request->only(['email','password']);
        if (! $token = auth()->attempt($credentials)) {
            return response()->json(['error' => 'Incorrect credentials'], 401);
        }
        return response()->json(compact('token'));
    }
    public function register(Request $request){
        // Expression Verification
        $this->validate($request,[
            'email' => 'required|email|max:255|unique:users',
            'name' => 'required|max:255',
            'password' => 'required|min:8|confirmed',
        ]);
        // Create users and generate Token
        $user =  User::create([
            'name' => $request->input('name'),
            'email' => $request->input('email'),
            'password' => Hash::make($request->input('password')),
        ]);
        $token = JWTAuth::fromUser($user);
        return response()->json(compact('token'));
    }
}

Этот шаг очень прост. Что нам нужно сделать, так это добавить аутентификацию и зарегистрировать Метод в наш контроллер. оставайтесь аутентифицируйтесь Методом, мы проверяем ввод, пытаемся войти в систему и возвращаем токен в случае успеха. оставайтесь регистрируйтесь Методом, мы проверяем ввод, затем создаем пользователя на его основе и генерируем токен.

4. Далее давайте перейдем к относительно простой части. Проверьте то, что мы только что написали. Используйте php artisan make:проверка подлинности теста Создание тестовых классов. В новом тесты/Функция/Проверка подлинности Добавьте следующий метод:

 '[email protected]',
        'name' => 'Test',
        'password' => 'secret1234',
        'password_confirmation' => 'secret1234',
    ];
    // Send a post request
    $response = $this->json('POST',route('api.register'),$data);
    // Determine whether the transmission was successful
    $response->assertStatus(200);
    // Receive our token
    $this->assertArrayHasKey('token',$response->json());
    // Delete data
    User::where('email','[email protected]')->delete();
}
/**
 * @test
 * Test login
 */
public function testLogin()
{
    // Creating Users
    User::create([
        'name' => 'test',
        'email'=>'[email protected]',
        'password' => bcrypt('secret1234')
    ]);
    // Simulated landing
    $response = $this->json('POST',route('api.authenticate'),[
        'email' => '[email protected]',
        'password' => 'secret1234',
    ]);
    // Determine whether the login is successful and receive token 
    $response->assertStatus(200);
    $this->assertArrayHasKey('token',$response->json());
    // Delete users
    User::where('email','[email protected]')->delete();
}

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

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

5. В этом уроке мы будем использовать рецепты в качестве нашей модели данных CRUD.

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

increments('id');
        $table->string('title');
        $table->text('procedure')->nullable();
        $table->tinyInteger('publisher_id')->nullable();
        $table->timestamps();
    });
}
public function down()
{
    Schema::dropIfExists('recipes');
}

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

belongsTo(User::class);
}

Затем добавьте этот метод в пользователь Модель.

hasMany(Recipe::class);
}

6. Теперь нам нужна последняя часть настройки, чтобы завершить управление рецептами. Сначала мы создадим контроллер php artisan make:контроллер Recipescontroller . Затем отредактируем маршруты/api.php Файл и добавить создать Конечные точки маршрутизации.

 ['api','auth'],'prefix' => 'recipe'],function (){
    Route::post('create','[email protected]')->name('recipe.create');
});

В контроллере добавьте метод создания

validate($request,['title' => 'required','procedure' => 'required|min:8']);
    // Create recipes and attach them to users
    $user = Auth::user();
    $recipe = Recipe::create($request->only(['title','procedure']));
    $user->recipes()->save($recipe);
    // Return recipe data in JSON format
    return $recipe->toJson();
}

Используйте php artisan make:тестовый рецепт теста Создавайте тесты функций и редактируйте содержимое следующим образом:

 'test',
            'email' => '[email protected]',
            'password' => Hash::make('secret1234'),
        ]);
        $token = JWTAuth::fromUser($user);
        return $token;
    }
  
    public function testCreate()
    {
        // Get token
        $token = $this->authenticate();
        $response = $this->withHeaders([
            'Authorization' => 'Bearer '. $token,
        ])->json('POST',route('recipe.create'),[
            'title' => 'Jollof Rice',
            'procedure' => 'Parboil rice, get pepper and mix, and some spice and serve!'
        ]);
        $response->assertStatus(200);
    }
}

Приведенный выше код может быть вам не очень понятен. Все, что мы сделали, это создали метод для обработки регистрации пользователей и генерации токенов, а затем test Create() Этот токен используется в методе. Обратите внимание на Обновить базу данных Особенность, эта особенность-удобный способ для Laravel сбрасывать базу данных после каждого теста, что очень подходит для нашего прекрасного небольшого проекта.

Итак, теперь нам просто нужно определить, находится ли текущий запрос в состоянии ответа, а затем продолжить выполнение? $ поставщик/бин/phpunit

Если все пройдет гладко, вы должны получить сообщение об ошибке. ?

Произошел 1 сбой:

1) Тестирует функцию Теста рецепта::тест Создает Ожидаемый код состояния 200, но получил 500. Не удалось утверждать, что ложь является истиной.

//главная/пользователь/сайты/tdd-путешествие/поставщик/laravel/фреймворк/src/Осветить/Основа/Тестирование/Тестовый ответ.php:133/главная/пользователь/сайты/tdd-путешествие/тесты/Функция/RecipeTest.php:49

НЕУДАЧИ! Тесты: 3, Утверждения: 5, Неудачи: 1.

Просматривая файл журнала, мы видим, что виновником является Рецепт и Пользователь Класс издатель и рецепты Отношения. Laravel пытается найти поле в таблице, которое является идентификатором пользователя Столбец _ _ _ _ _ _ _ используется в качестве внешнего ключа, но в нашей миграции мы publisher_id Установим значение внешний ключ. Теперь отрегулируйте линию так, чтобы:

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

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

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

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

authenticate();
$response = $this->withHeaders([
    'Authorization' => 'Bearer '. $token,
])->json('POST',route('recipe.create'),[
    'title' => 'Jollof Rice',
    'procedure' => 'Parboil rice, get pepper and mix, and some spice and serve!'
]);
$response->assertStatus(200);
// Get counts and make judgments.
$count = User::where('email','[email protected]')->first()->recipes()->count();
$this->assertEquals(1,$count);

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

 ['api','auth'],'prefix' => 'recipe'],function (){
    Route::post('create','[email protected]')->name('recipe.create');
    Route::get('all','[email protected]')->name('recipe.all');
    Route::post('update/{recipe}','[email protected]')->name('recipe.update');
    Route::get('show/{recipe}','[email protected]')->name('recipe.show');
    Route::post('delete/{recipe}','[email protected]')->name('recipe.delete');
});

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

validate($request,['title' => 'required','procedure' => 'required|min:8']);
    // Create recipes并附加到用户
    $user = Auth::user();
    $recipe = Recipe::create($request->only(['title','procedure']));
    $user->recipes()->save($recipe);
    // Returns the JSON format data for the recipe
    return $recipe->toJson();
}
// Get all the recipes
public function all(){
    return Auth::user()->recipes;
}
// Renewal of formula
public function update(Request $request, Recipe $recipe){
    // Check if the user is the owner of the recipe?
    if($recipe->publisher_id != Auth::id()){
        abort(404);
        return;
    }
    // Update and return
    $recipe->update($request->only('title','procedure'));
    return $recipe->toJson();
}
// Display details of individual recipes
public function show(Recipe $recipe){
    if($recipe->publisher_id != Auth::id()){
        abort(404);
        return;
    }
    return $recipe->toJson();
}
// Delete a formula
public function delete(Recipe $recipe){
    if($recipe->publisher_id != Auth::id()){
        abort(404);
        return;
    }
    $recipe->delete();
}

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

Наконец, наш тест/Функция/Тест рецепта :

 'test',
        'email' => '[email protected]',
        'password' => Hash::make('secret1234'),
    ]);
    $this->user = $user;
    $token = JWTAuth::fromUser($user);
    return $token;
}
// Test Creation Routing
public function testCreate()
{
    // Getting tokens
    $token = $this->authenticate();
    $response = $this->withHeaders([
        'Authorization' => 'Bearer '. $token,
    ])->json('POST',route('recipe.create'),[
        'title' => 'Jollof Rice',
        'procedure' => 'Parboil rice, get pepper and mix, and some spice and serve!'
    ]);
    $response->assertStatus(200);
    // Get counts and assert
    $count = $this->user->recipes()->count();
    $this->assertEquals(1,$count);
}
// Tests show all routes
public function testAll(){
    // Verify and attach recipes to users
    $token = $this->authenticate();
    $recipe = Recipe::create([
        'title' => 'Jollof Rice',
        'procedure' => 'Parboil rice, get pepper and mix, and some spice and serve!'
    ]);
    $this->user->recipes()->save($recipe);
    // Call routing and assert response
    $response = $this->withHeaders([
        'Authorization' => 'Bearer '. $token,
    ])->json('GET',route('recipe.all'));
    $response->assertStatus(200);
    // The assertion count is 1, and the title of the first item is related
    $this->assertEquals(1,count($response->json()));
    $this->assertEquals('Jollof Rice',$response->json()[0]['title']);
}
// Test Update Routing
public function testUpdate(){
    $token = $this->authenticate();
    $recipe = Recipe::create([
        'title' => 'Jollof Rice',
        'procedure' => 'Parboil rice, get pepper and mix, and some spice and serve!'
    ]);
    $this->user->recipes()->save($recipe);
    // Call routing and assert response
    $response = $this->withHeaders([
        'Authorization' => 'Bearer '. $token,
    ])->json('POST',route('recipe.update',['recipe' => $recipe->id]),[
        'title' => 'Rice',
    ]);
    $response->assertStatus(200);
    // The assertion heading is the new heading
    $this->assertEquals('Rice',$this->user->recipes()->first()->title);
}
// Testing a single display routing
public function testShow(){
    $token = $this->authenticate();
    $recipe = Recipe::create([
        'title' => 'Jollof Rice',
        'procedure' => 'Parboil rice, get pepper and mix, and some spice and serve!'
    ]);
    $this->user->recipes()->save($recipe);
    $response = $this->withHeaders([
        'Authorization' => 'Bearer '. $token,
    ])->json('GET',route('recipe.show',['recipe' => $recipe->id]));
    $response->assertStatus(200);
    // To assert that the title is correct
    $this->assertEquals('Jollof Rice',$response->json()['title']);
}
// Test Delete Routing
public function testDelete(){
    $token = $this->authenticate();
    $recipe = Recipe::create([
        'title' => 'Jollof Rice',
        'procedure' => 'Parboil rice, get pepper and mix, and some spice and serve!'
    ]);
    $this->user->recipes()->save($recipe);
    $response = $this->withHeaders([
        'Authorization' => 'Bearer '. $token,
    ])->json('POST',route('recipe.delete',['recipe' => $recipe->id]));
    $response->assertStatus(200);
    // To assert that there is no recipe
    $this->assertEquals(0,$this->user->recipes()->count());
}

В дополнение к дополнительным тестам мы добавили атрибут $user с областью действия класса. Таким образом, мы можем не только использовать его. $пользователь Для использования аутентификации Метод генерирует не только токены, но и другие пары для последующих пар $user Операция готова.

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

заключение

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

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

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

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

От: https://learnku.com/laravel/t…

Дополнительные статьи: https://learnku.com/laravel/c…