>  기사  >  백엔드 개발  >  Sanctum을 사용하여 Laravel에서 단위 테스트

Sanctum을 사용하여 Laravel에서 단위 테스트

PHPz
PHPz원래의
2024-09-07 10:30:321053검색

Pruebas Unitarias en Laravel con Sanctum

잘 지내요 우리 아이들, 즐거운 시간을 보내고 좋은 한 주를 보내고 더 나은 한 달을 보내길 바랍니다. 저는 thedevgang.com에 이 게시물을 썼고 여러분 모두의 참여가 더 많아지도록 여기에서 공유합니다. 마음에 드셨으면 좋겠어요 :3

벌써 2024년의 마지막 이정표라던가 지금으로서는 이야기할 가치도 없는 일들입니다. 이전 블로그 게시물에서는 Passport 인증 라이브러리를 Sanctum으로 마이그레이션했지만 이제는 일부 엔드포인트의 단위 테스트를 자세히 살펴보고 이를 Github Actions와 같은 지속적인 통합 파이프라인에서 실행할 수 있도록 하겠습니다. 🎜>

이전에 dev.to에서 Passport를 사용하여 단위 테스트를 수행하는 방법에 대해 작성한 적이 있습니다. 이 게시물은 여기에서 찾을 수 있으며, 단위 테스트가 무엇인지와 Laravel에서의 구현에 대한 기본 측면도 설명합니다. 이 게시물에서는 다음 내용을 다룰 것입니다:

    Sanctum이 이미 구현된 단위 테스트
  • 일부 엔드포인트 테스트
Sanctum을 사용한 단위 테스트 구현

이 게시물의 경우 제가 몇 달 동안 개발해 온 대안 프로젝트를 위해 몇 가지 엔드포인트를 정리했습니다. 본 프로젝트는 프레임워크 및 기타 측면에서 다음과 같은 특징을 가지고 있습니다.

    Laravel 11과 Sanctum 4
  • PHPUnit 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

이렇게 하면 테스트/유닛 폴더에 다음과 같이 완전히 "비어 있는" 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. 올바른 프로필 등록 및 피드백
사용 사례가 나열되면 앞서 언급한 방법이 이 경우에 다루는 내용이 Case 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:

  • 出现问题的方法,检查是否是代码情况。
  • 检查 PHPUnit 配置是否合适,我们将在下一篇文章中深入研究。

同样,我将解释如何配置 Github Actions 以对其运行单元测试,甚至能够获取代码覆盖率报告和可能的持续部署。我希望这篇文章虽然很长,但能为您提供有关单元测试以及持续集成和部署过程的更多背景信息。

编码愉快!

위 내용은 Sanctum을 사용하여 Laravel에서 단위 테스트의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.