Laravel 與 Sanctum 的單元測試

PHPz
發布: 2024-09-07 10:30:32
原創
1072 人瀏覽過

Pruebas Unitarias en Laravel con Sanctum

怎麼樣,我的孩子們,我希望你們度過了愉快的時光,度過了美好的一周,甚至是更好的一個月。我在 thedevgang.com 上寫了這篇文章,並在這裡分享它,以便與大家有更多的互動。我希望你喜歡它:3

已經是2024年的最後一個里程碑了,其他的事情,此時不值得談論。好吧,在之前的部落格文章中,我們將 Passport 身份驗證庫遷移到了 Sanctum,但是現在,我想深入研究一些端點的單元測試,從而能夠在持續集成管道(例如 Github Actions)中執行它們

之前,我在 dev.to 中寫過如何使用 Passport 進行單元測試,這篇文章可以在這裡找到,我還解釋了什麼是單元測試以及它們在 Laravel 中實現的基本方面。在這篇文章中,我們將介紹以下內容:

    已實作 Sanctum 的單元測試
  • 測試一些端點
實作 Sanctum 的單元測試

在這篇文章中,我為我已經開發了幾個月的替代項目整理了一些端點。此項目在框架等方面具有以下特點:

    Laravel 11 與 Sanctum 4
  • PHP 單元 10
  • Laravel Sail 作為開發環境
在這種情況下,我們將測試為此應用程式的身份驗證過程設定的三個端點,首先我們將使用以下方法執行適當的操作:


public function login(Request $request)
    {
        $validator = Validator::make($request->all(), [
            'email' => 'required|email',
            'password' => 'required',
            'device_id' => 'required',
        ]);

        if ($validator->fails()) {
            return response()->json(['success' => false, 'error' => $validator->errors()], $this->badRequestStatus);
        }

        $result = $this->getToken(request('email'), request('password'), request('device_id'));

        if ($result['success'] == true) {
            return response()->json($result, $this->successStatus);
        } else {
            return response()->json(['success' => false, 'error' => 'Unauthorized'], $this->unauthorizedStatus);
        }
    }
登入後複製
這個方法完全管理我們應用程式的登入過程,但是註冊不包含在這個程式碼片段中,這將是下一個要測試的方法。在這種情況下,我們已經確認了它並且似乎可以正常工作,但為了確保這一點,我們將設定各自的測試。

首先在終端機輸入此指令:

php artisan make:test UserTest --unit

這將在tests/Unit資料夾中建立一個UserTest文件,該文件將完全“空白”,如下所示:


<?php

namespace Tests\Unit;

use PHPUnit\Framework\TestCase;

class ExampleTest extends TestCase
{
    /**
     * A basic test example.
     */
    public function test_basic_test(): void
    {
        $this->assertTrue(true);
    }
}
登入後複製
刪除 test_basic_test() 方法,我們不再需要它了。在這種情況下,我說它是空白的,因為它只是我們單元測試的模擬,在這種情況下,它將是我們用於上述方法的測試。現在,在開始安排測試之前,我們需要確保我們將執行和測試的用例,因此我們有以下用例要測試:

    登入正確。
  1. 輸入所有資料的登入無效。
  2. 正確註冊。
  3. 正確的個人資料註冊。
  4. 由於未輸入資料而導致個人資料註冊錯誤。
  5. 找不到個人資料。
  6. 註冊正確的個人資料及其回饋。
列出用例後,我們考慮到上述方法在本例中涵蓋的是用例 1 和 2,因此我們將繼續處理它們。

測試準備

現在,在開始編寫測試程式碼之前,我們需要配置它們以便它們能夠正確執行。為此,我們將在 UserTest 檔案中建立 setUp 方法,該方法在執行單元測試之前執行指令。 。在這裡,我們可以告訴系統必須執行遷移,並且在需要資料時能夠開始遷移,以及為變數賦值。我們將建立的 setUp 方法的結構如下:


public function setUp(): void
    {
        parent::setUp();
        $this->faker = \Faker\Factory::create();

        $this->name = $this->faker->name();
        $this->password = 'password';
        $this->email = 'valid@test.com';
        $this->deviceId = $this->faker->uuid();

        Artisan::call('migrate:fresh', ['-vvv' => true]);
    }
登入後複製
設定將執行以下操作:

    建立Faker的實例,這是一個模擬各種類型變數的資料輸入的函式庫。
  • 我們創造了一個虛構的名字
  • 我們將密碼和電子郵件指派為預設值。
  • 我們也為偽造者分配了一個虛構的設備 ID。
  • 將運行資料庫遷移
在此方法之上,聲明我們將用於所有測試的全域變數:


public $faker;
public $name;
public $email;
public $password;
public $deviceId;
登入後複製
單元測試的開發

對於測試 1,我們需要透過呼叫我們將在應用程式中呼叫的端點來確保登入正確。我們將建立 test_login_success 方法,它看起來像這樣:


public function test_login_success()
    {
        Artisan::call('db:seed', ['-vvv' => true]);

        $body = [
            'email' => $this->email,
            'password' => $this->password,
            'device_id' => $this->deviceId
        ];

        $this->json('POST', '/api/login', $body, ['Accept' => 'application/json'])
            ->assertStatus(200)->assertJson([
                "success" => true
            ]);
    }
登入後複製

Este método, primeramente alimentará la base de datos con los catálogos pertinentes para poder confirmar que los mismos existen sin problemas. Después asignará el body y enviará los datos por medio de un request POST, al enviarlo, revisará que el status que devuelva su llamada es 200 y que los datos sean conforme al arreglo solicitado para confirmar, en este caso [ “success” => true ]. Si todo sale bien y se cumplen las condiciones, se considera prueba satisfactoria, en caso contrario, se considerará fallida y es donde se tendrá que revisar nuevamente el código.

Ahora bien, haremos el caso de uso 2. Para ello crea un método llamado test_login_error_with_data_ok e ingresa el siguiente código:

public function test_login_error_with_data_ok()
    {
        Artisan::call('db:seed', ['-vvv' => true]);

        $body =  [
            'email' => 'invalid@test.com',
            'password' => 'password',
            'device_id' => $this->deviceId
        ];

        $this->json('POST', '/api/login', $body)
            ->assertStatus(401)->assertJson([
                "success" => false
            ]);
    }
登入後複製

A diferencia del anterior, en este caso, se le entregan datos erróneos y se solicita que confirme que el endpoint devuelva un error 401, así como un body [“success” => false ], esto con el fin de que se confirme que el sistema deniega el acceso a alguien que no tenga credenciales correctas.

Con esto, cubrimos el método presentado anteriormente y ya quedaría cubierto el método. Para poder probarlo, podemos ejecutar el siguiente comando bajo Sail:

docker compose exec laravel.test php artisan test

Te mostrará los siguientes resultados:

PASS  Tests\Unit\UserTest
  ✓ login error with data ok 0.08s  
  ✓ login success 0.16s
登入後複製

Si te sale todo bien como te lo he mostrado, tus unit tests han salido satisfactoriamente, pero estamos lejos de terminar. Ahora necesitamos probar el siguiente método:

public function register(Request $request)
    {
        $validator = Validator::make($request->all(), [
            'email' => 'required|email|unique:users',
            'password' => 'required',
            'c_password' => 'required|same:password',
            'device_id' => 'required',
        ]);

        if ($validator->fails()) {
            return response()->json(['success' => false, 'error' => $validator->errors()], $this->badRequestStatus);
        }

        $password = $request->password;
        $input = $request->all();
        $input['password'] = bcrypt($password);
        $user = User::create($input);

        if (null !== $user) {
            $result = $this->getToken($user->email, $password, $request->device_id);

            if ($result['success'] == true) {
                return response()->json($result, $this->successStatus);
            } else {
                return response()->json(['success' => false, 'error' => 'Unauthorized'], $this->unauthorizedStatus);
            }
        }
    }
登入後複製

En este caso, realizaremos el caso de uso 3, el cual solicita confirmar que el registro sea correcto, para ello, crea el método test_register_success e ingresa el siguiente código:

public function test_register_success()
    {
        $body = [
            'name' => $this->name,
            'email' => $this->email,
            'password' => $this->password,
            'c_password' => $this->password,
            'device_id' => $this->deviceId
        ];

        $this->json('POST', '/api/register', $body)
            ->assertStatus(200)->assertJson([
                "success" => true
            ]);
    }
登入後複製

Al igual que con el login, solicitamos que nos confirme el sistema que se nos está entregando un código 200 así como el arreglo [“success” => true], si logramos eso, ya hemos terminado, pero si te das cuenta, nos hace falta la prueba en caso de que se equivoque el usuario. Ese método te lo dejo de tarea para que puedas corroborar tus conocimientos.

Ahora bien probaremos los siguientes métodos:

public function profile()
    {
        $user = Auth::user();
        $profile = Profile::find($user->id);

        if (null !== $profile) {
            return response()->json(["success" => true, "data" => $user], $this->successStatus);
        } else {
            return response()->json(['success' => false, 'message' => 'Usuario no encontrado.'], $this->notFoundStatus);
        }
    }
登入後複製
public function createProfile(Request $request)
    {
        try {
            $validator = Validator::make($request->all(), [
                'first_name' => 'required',
                'last_name' => 'required',
                'birth_date' => 'required|date',
                'bloodtype' => 'required|numeric',
                'phone' => 'required',
                'gender' => 'required|numeric',
                'country' => 'required|numeric',
                'state' => 'required|numeric',
            ]);

            if ($validator->fails()) {
                return response()->json(['success' => false, 'error' => $validator->errors()], $this->badRequestStatus);
            }

            $user = Auth::user();
            $profile = Profile::where(['user_id' => $user->id])->first();

            $data = [
                'user_id' => $user->id,
            ];

            $dataInsert = array_merge($data, $request->all());

            if (null !== $profile) {
                $profile = $profile->update($dataInsert);
            } else {
                $profile = Profile::create($dataInsert);
            }


            return response()->json(["success" => true, "message" => 'Perfil actualizado correctamente.'], $this->successStatus);
        } catch (QueryException $e) {
            return response()->json(["success" => false, "message" => 'Error al actualizar el perfil.'], $this->internalServerErrorStatus);
        }
    }
登入後複製

Este par de métodos son los referentes a la gestión del perfil del usuario y su retroalimentación, por lo que los casos de uso que debemos probar son del 4 al 7. Para el caso 4, debemos crear un nuevo método llamado test_register_profile_success y agregamos el siguiente código:

public function test_register_profile_success()
    {
        $body = [
            'first_name' => $this->faker->firstName,
            'last_name' => $this->faker->lastName,
            'birth_date' => '1987-10-10',
            'bloodtype' => 1,
            'phone' => $this->faker->phoneNumber,
            'gender' => 1,
            'country' => 1,
            'state' => 1,
        ];

        $user = User::factory()->create();
        $token = $user->createToken('TestToken')->plainTextToken;

        $response = $this->withHeaders([
            'Authorization' => 'Bearer ' . $token,
        ])->post('/api/user/profile', $body);

        $response->assertStatus(200);
    }
登入後複製

En esta ocasión, necesitamos declarar un arreglo que simule el contenido del cuerpo del request para que pueda ser enviado correctamente por el endpoint y una vez enviado, el confirmar que el request tiene una respuesta satisfactoria (200).

Para el caso del perfil erróneo por no ingresar datos, necesitamos agregar un nuevo método que denominaremos test_register_profile_validation_failed, el cual implementaremos de la siguiente forma:

public function test_register_profile_validation_failed()
    {
        $user = User::factory()->create();
        $token = $user->createToken('TestToken')->plainTextToken;

        $response = $this->withHeaders([
            'Authorization' => 'Bearer ' . $token,
        ])->post('/api/user/profile', []);

        $response->assertStatus(400);
    }
登入後複製

En este caso, es prácticamente el mismo contenido de la prueba anterior, con la diferencia que ahora le enviamos un arreglo en blanco, para poder asegurarnos que si no se están enviando los datos correctamente, no permita la creación del perfil del usuario por medio de un Bad Request error (400).

El siguiente método probará que en caso de no encontrar el perfil de algún usuario, así lo indique con un código 404, por lo que creamos otro método denominado test_obtain_profile_not_found e ingresando el siguiente código.

public function test_obtain_profile_not_found()
    {
        $user = User::factory()->create();
        $token = $user->createToken('TestToken')->plainTextToken;

        $response = $this->withHeaders([
            'Authorization' => 'Bearer ' . $token,
        ])->get('/api/user/profile');

        $response->assertStatus(404);
    }
登入後複製

En el modelo de negocio, nosotros al registrarnos, creamos el usuario, mas no el perfil que tiene que ser ingresado posteriormente, por lo que al momento de ejecutar la prueba unitaria, al ejecutar el request para obtener el perfil, nos enviará un código 404, comportamiento que estamos buscando para esta prueba unitaria.

Finalmente para el último caso de uso, crearemos el método test_register_profile_and_obtain para confirmar que un mismo test pueda obtener dos comportamientos en un mismo flujo. Para este caso implementaremos el siguiente código:

public function test_register_profile_and_obtain()
    {
        $body = [
            'first_name' => $this->faker->firstName,
            'last_name' => $this->faker->lastName,
            'birth_date' => '1987-10-10',
            'bloodtype' => 1,
            'phone' => $this->faker->phoneNumber,
            'gender' => 1,
            'country' => 1,
            'state' => 1,
        ];

        $user = User::factory()->create();
        $token = $user->createToken('TestToken')->plainTextToken;

        $this->withHeaders([
            'Authorization' => 'Bearer ' . $token,
        ])->post('/api/user/profile', $body);

        $response = $this->withHeaders([
            'Authorization' => 'Bearer ' . $token,
        ])->get('/api/user/profile');

        $response->assertStatus(200);
    }
登入後複製

En este test, implementamos dos casos de uso realizados previamente, el primero es la creación del perfil y posteriormente, retroalimentamos el perfil, indicando a PHPUnit que deseamos confirmar que el response del endpoint que retroalimenta el perfil sea satisfactoria (código 200). Igualmente podríamos realizar el assert de la inserción de datos cambiando algunas líneas de código, pero por el momento es más que suficiente.

Ya terminando las pruebas unitarias, procedemos a ejecutar el comando docker compose exec laravel.test php artisan test y confirmamos el estatus de nuestras pruebas unitarias. Si nos salen de esta forma:

PASS  Tests\Unit\UserTest
  ✓ login error with data ok.                 0.10s  
  ✓ login success.                            0.15s  
  ✓ register success.                         0.20s  
  ✓ register profile success.                 0.10s  
  ✓ register profile validation failed.       0.09s  
  ✓ obtain profile not found.                 0.10s  
  ✓ register profile and obtain.              0.10s  
登入後複製

Las pruebas unitarias salieron satisfactorias. En caso contrario, checa lo siguiente:

  • La méthode qui a eu des problèmes, vérifiez qu'il ne s'agit pas d'une situation de code.
  • Vérifiez que la configuration de PHPUnit est appropriée, nous y reviendrons dans le prochain post.

De même, je vais vous expliquer comment configurer Github Actions pour y exécuter des tests unitaires et même pouvoir obtenir des rapports de couverture de code et un éventuel déploiement continu. J'espère que cet article, bien que long, servira à vous donner plus de contexte sur les tests unitaires et sur un processus d'intégration et de déploiement continu.

Bon codage !

以上是Laravel 與 Sanctum 的單元測試的詳細內容。更多資訊請關注PHP中文網其他相關文章!

來源:dev.to
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板