Your Web News in One Place

Help Webnuz

Referal links:

Sign up for GreenGeeks web hosting
September 13, 2021 05:57 pm GMT

Pruebas Unitarias en Autenticacin con Laravel y Passport

Mis nios, la verdad, as como los he dejado solos un buen rato, ahora ando inspirado para publicar algo que me trajo muchos dolores de cabeza: las pruebas unitarias con Passport, no sin antes, quiero felicitarlos hoy por el da del programador a todos y cada uno de mis colegas y de la comunidad DEV en espaol.

Para los que no conocen Passport, es una librera dentro de Laravel que te permite realizar autenticacin con OAuth 2 mediante Personal Access Tokens o por medio de las clsicas JWT junto con una Refresh Token para mantener el login el mayor tiempo posible.

El rollo de implementar UT

ANGRY GERMAN KID

Cuando uno est acostumbrado a desarrollar sin Unit Testing, por lo general se vuelve un dolor de cabeza el implementarlas, sin embargo conforme va uno agarrando el hilo de las mismas, adems de que es una economa al momento de poner en produccin un desarrollo te da la certeza de que desarrollaste un mdulo correctamente.

Pero antes que nada dirn los que empiezan qu es una prueba unitaria? Ah pues como lo dice la gran amiga Wikipedia una prueba unitaria es una forma de comprobar el correcto funcionamiento de una unidad de cdigo.

Entonces, cuando nosotros hacemos algn comportamiento dentro de un sistema, tenemos que pasarlo por pruebas diversas, sin embargo, las que nos alertan desde un principio si estn bien son las unitarias. En PHP (lenguaje de Laravel) usamos lo que es PHPUnit para realizar las mismas y este por lo general ya viene incluido cuando instalas Laravel.

El tema viene cuando quieres hacer unit testing con Passport. Puesto que en uno de los pasos, tienes que llamar al servidor para generar tus respectivas tokens. Sin embargo sucede que tu UT va a tronar si careces de un servidor web implementado en la misma. Veamos qu sucede.

Ahora bien, si quieres saber ms a fondo sobre cmo implementar Passport en Laravel, te dejo este link, mismo que es en el que me bas para armar este post.

El cdigo a testear

Primeramente, tomamos en cuenta el siguiente mtodo:

public function getAuthAndRefreshToken($email, $password) {         $oClient = OClient::where('password_client', 1)->first();        $http = new Client;        $oauthUrl = (env('APP_ENV') != 'local') ? route('passport.token') : env('APP_URL') . 'oauth/token';        $response = $http->request('POST', $oauthUrl, [            'form_params' => [                'grant_type' => 'password',                'client_id' => $oClient->id,                'client_secret' => $oClient->secret,                'username' => $email,                'password' => $password,                'scope' => '*',            ],        ]);        $result = json_decode((string) $response->getBody(), true);        return response()->json($result, $this->successStatus);    }

La implementacin en el mtodo a testear:

public function login(Request $request){        $validator = Validator::make($request->all(), [            'email' => 'email|required',            'password' => 'required'        ]);        if($validator->fails()) {            return response()->json(['success' => false, 'details' => $validator->errors()], 401);        }        $result = $this->getAuthAndRefreshToken(request('email'), request('password'));        if($result['success'] == true) {            return response()->json($result, 200);        } else {             return response()->json(['success' => false, 'error'=>'Unauthorized'], 401);         }     }

Y su respectiva UT:

   public function test_login_success() {        $user = User::factory()->make();        $response = $this->postJson('/api/login', [            'email' => $user->email,            'password' => 'password'        ]);        $response->assertStatus(200)->assertJson([            'success' => true        ]);    }

Al momento de nosotros realizar nuestras pruebas unitarias, van a fallar irremediablemente y la razn es de que no hay como tal una implementacin de un servidor web existente dentro de PHPUnit. Nosotros para poder realizar nuestras pruebas, tenemos que simular el request dentro del primer mtodo y precisamente el bloque siguiente:

$response = $http->request('POST', $oauthUrl, [            'form_params' => [                'grant_type' => 'password',                'client_id' => $oClient->id,                'client_secret' => $oClient->secret,                'username' => $email,                'password' => $password,                'scope' => '*',            ],        ]);

es el que nos est dando lata :(. Si lo pasas por un Github Actions, va a tronar riqusimo marcndote que no existe la URL que tienes en la siguiente lnea:

$oauthUrl = (env('APP_ENV') != 'local') ? route('passport.token') : env('APP_URL') . 'oauth/token';

La solucin a todos nuestros problemas

Primeramente, el mtodo que tenemos que refactorizar es el de getAuthAndRefreshToken. Tenemos que cambiar el modo como llamamos el request y que sea funcional tanto en la implementacin como en las pruebas.

La forma de realizarlo es:

  1. Asignando al request principal los parmetros que el endpoint OAuth requiere para armar un nuevo request.
  2. Despachar una ruta en POST: el endpoint oauth/token por medio de un nuevo Request interno.

Lo anterior sin utilizar cliente alguno como Guzzle o similares, mismas que requieren tener un servidor Apache o nginx implementado, features que no se utilizan en pruebas unitarias.

Para ello cambiamos a lo siguiente:

private function getAuthAndRefreshToken(Request $request, $email, $password) {        $oClient = OClient::where('password_client', env('DEFAULT_PASSWORD_CLIENT_ID', 1))->first();        $request->request->add([            'grant_type' => 'password',            'client_id' => $oClient->id,            'client_secret' => $oClient->secret,            'username' => $email,            'password' => $password,            'scope' => '*',        ]);        $response = Route::dispatch(Request::create('oauth/token', 'POST'));        $result = json_decode((string) $response->getContent(), true);        $result['success'] = (isset($result['error'])) ? false : true;        return $result;    }

En este refactor, hemos cambiado varias cosas:

  1. Ya no necesitars una URL ni un cliente para armar el request. Es ms sano, pues ya as puedes asignar la implementacin sin importar la URL o factores externos al mtodo y puedes testear sin problemas desde consola.

  2. Obtienes mayores detalles de la respuesta que te da Passport al momento de llamar el endpoint de OAuth para poder dar ms transparencia al proceso, igualmente ya podrs hacer manejo de errores del mtodo sin problemas.

Y ya con este refactor, tendras que cambiar la implementacin en tu mtodo login, concretamente el inicio del mtodo ya sera as:

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

Al terminar todo esto, puedes pasar tus unit testings de forma manual en tu cdigo o por Github Actions para poder confirmar las mismas y ya proceder a realizar implementaciones adicionales.

Y pues bien, esto era lo que me traa de los pelos en cuestiones de pruebas unitarias y quiero compartirlo con ustedes para que puedan sacar ms rpido este tema con Passport. Ahora, no olvides de reaccionar y compartirlo con los colegas para que haya ms y mejores posts en espaol dentro de DEV.

Happy coding!


Original Link: https://dev.to/rzerostern/pruebas-unitarias-en-autenticacion-con-laravel-y-passport-97h

Share this article:    Share on Facebook
View Full Article

Dev To

An online community for sharing and discovering great ideas, having debates, and making friends

More About this Source Visit Dev To