잘 지내요 우리 아이들, 즐거운 시간을 보내고 좋은 한 주를 보내고 더 나은 한 달을 보내길 바랍니다. 저는 thedevgang.com에 이 게시물을 썼고 여러분 모두의 참여가 더 많아지도록 여기에서 공유합니다. 마음에 드셨으면 좋겠어요 :3
벌써 2024년의 마지막 이정표라던가 지금으로서는 이야기할 가치도 없는 일들입니다. 이전 블로그 게시물에서는 Passport 인증 라이브러리를 Sanctum으로 마이그레이션했지만 이제는 일부 엔드포인트의 단위 테스트를 자세히 살펴보고 이를 Github Actions와 같은 지속적인 통합 파이프라인에서 실행할 수 있도록 하겠습니다. 🎜>
이전에 dev.to에서 Passport를 사용하여 단위 테스트를 수행하는 방법에 대해 작성한 적이 있습니다. 이 게시물은 여기에서 찾을 수 있으며, 단위 테스트가 무엇인지와 Laravel에서의 구현에 대한 기본 측면도 설명합니다. 이 게시물에서는 다음 내용을 다룰 것입니다:
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() 메서드를 삭제하면 필요하지 않습니다. 이 경우에는 단위 테스트의 모의일 뿐이므로 비어 있다고 말하고 이 경우에는 앞서 언급한 방법에 사용하는 것이 될 것입니다. 이제 테스트 일정을 시작하기 전에 실행하고 테스트할 사용 사례를 확인해야 하므로 테스트할 사용 사례는 다음과 같습니다.
시험 준비
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]); }설정은 다음을 수행합니다:
public $faker; public $name; public $email; public $password; public $deviceId;단위 테스트 개발
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:
同样,我将解释如何配置 Github Actions 以对其运行单元测试,甚至能够获取代码覆盖率报告和可能的持续部署。我希望这篇文章虽然很长,但能为您提供有关单元测试以及持续集成和部署过程的更多背景信息。
编码愉快!
위 내용은 Sanctum을 사용하여 Laravel에서 단위 테스트의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!