TDD API REST con Lumen

Lucas Michailian
Aug 8, 2017 · 4 min read

Lumen es el microframework de Laravel, el mismo no contiene tantas dependencias y paquetes propios haciendo así muy veloz para el uso de api’s.

En esta entrega vamos a crear un simple CRUD de usuarios y vamos a usar la suite de testeo PHPunit para generar test funcionales.

Asumiendo que ya instalaste y configuraste Lumen 5.4 vamos a seguir adelante.

Configurando PHPunit

En la raíz del proyecto vamos a encontrar un archivo llamado phpunit.xml el cual es el encargado de manejar todas las instrucciones para la suit de testeo.

En nuestro caso solo vamos a agregar dos lineas en la seccion <php>

<env name="DB_CONNECTION" value="sqlite">
<env name="DB_DATABASE" value=":memory:">

Estas lineas se encargaran de setear la base de datos a utilizar y que se almacene en memoria.

Migración de usuarios

Lumen por defecto ya trae una migración que nos es mas que util, la ejecutamos con el comando php artisan migrate

Rutas

Para esta sencilla aplicación solo necesitaremos algunas rutas sencillas para manejar nuestro crud.

web.php$app->get('users', [
'as' => 'users.index',
'uses' => 'UsersController@index'
]);

$app->post('users', [
'as' => 'users.store',
'uses' => 'UsersController@store'
]);

$app->get('users/{id}', [
'as' => 'users.show',
'uses' => 'UsersController@show'
]);

$app->put('users/{id}', [
'as' => 'users.update',
'uses' => 'UsersController@update'
]);

$app->delete('users/{id}', [
'as' => 'users.delete',
'uses' => 'UsersController@destroy'
]);

Mas info sobre rutas en lumen

Users Controller

Antes de configurar nuestro controller en lo personal reemplazo el base controller llamado “Controller” por “ApiController” es un clase que me permitirá tener algunos helpers y ser lo mas DRY posible, el mismo lo puedes hacer a tu manera es opcional.

<?php

namespace
App\Http\Controllers;

use Illuminate\Http\Request;

class ApiController extends Controller
{
/**
* Formatted success response helper
*
*
@param array $data
*
@param int $code
*
@return \Illuminate\Http\JsonResponse
*/
public function responseSuccess($data = [], $code = 200)
{
return response()
->json(['code' => $code,
'success' => true,
'data' => $data]);
}

/**
* Formatted failed response helper
*
*
@param array $data
*
@param int $code
*
@return \Illuminate\Http\JsonResponse
*/
public function responseFail($data = [], $code = 500)
{
return response()
->json(['code' => $code,
'success' => false,
'data' => $data]);
}
}

Una vez realizado esto el UsersController consta de:

<?php

namespace
App\Http\Controllers;

use App\User;
use Illuminate\Http\Request;

class UsersController extends ApiController
{
/**
* Display a listing of the resource.
*
*
@return \Illuminate\Http\Response
*/
public function index()
{
$users = User::all();

return $this->responseSuccess($users);
}

/**
* Store and validate newly created resource in storage.
*
*
@param \Illuminate\Http\Request $request
*
@return \Illuminate\Http\Response
*/
public function store(Request $request)
{
$this->validate($request, [
'name' => 'required',
'email' => 'required|email'
]);

$users = User::create($request->all());

return $this->responseSuccess($user);
}

/**
* Display the specified resource.
*
*
@param int $id
*
@return \Illuminate\Http\Response
*/
public function show($id)
{
$category = User::findOrFail($id);

return $this->responseSuccess($user);
}

/**
* Update the specified resource in storage.
*
*
@param \Illuminate\Http\Request $request
*
@param int $id
*
@return \Illuminate\Http\Response
*/
public function update(Request $request, $id)
{
$user = User::findOrFail($id);

$this->validate($request, [
'name' => 'required',
'email' => 'required|email'
]);

$result = $user->update($request->all());

if (!$result) {
return $this->responseFail();
}

return $this->responseSuccess($user);
}

/**
* Remove the specified resource from storage.
*
*
@param int $id
*
@return \Illuminate\Http\Response
*/
public function destroy($id)
{
$user = User::findOrFail($id);

$user->delete();

return $this->responseSuccess();
}
}

Dos aclaraciones:

  • Cuando las validaciones no pasen automaticamente Lumen devolvera una respuesta con codigo 422
  • Cuando una entidad no es encontrada Lumen devolverá 404 para esto es necesario usar el metodo findOrFail.

Model factory

Otro de los preparativos que debemos hacer es preparar nuestro modelo de prueba, este nos será útil a la hora de testear con usuarios fake y no tener que recurría a cargar continuamente un user en un cliente ej: Postman.

ModelFactory.php$factory->define(App\User::class, function (Faker\Generator $faker) {
return [
'name' => $faker->name,
'email' => $faker->email,
];
});

Ejecutando nuestro primer test

Lumen trae un ExampleTest.php el cual podemos ejecutar con el comando:

php vendor/bin/phpunit

y el resultado sera:

Creando nuestro primer

php artisan make:test <Nombre>Test

Es muy importante que el nombre al final contenga la palabra Test y este ubicado en la carpeta tests.

Para nuestro test de usuarios:

  • Los nombres de los metodos deben ser descriptivos
  • Deben tener el comentario /** @test **/ para que la suit los reconozca como tal.
<?php

use
Laravel\Lumen\Testing\DatabaseMigrations;

class UsersCrudTest extends TestCase
{
// Trait importante para que una vez ejecutado el test
// refresque las migraciones
use DatabaseMigrations;

/** @test **/
public function a_user_may_see_all_records()
{
// Con make no se persistirá en la db por lo tanto
//users tendra un id asociado y los podremos listar


$users = factory('App\User')->create();
$this->json('get', "{$this->api}/users")
->assertResponseStatus(200);
}

/** @test */
public function a_user_may_save_records()
{
// Con el metodo make no se persistirá en la db por lo tanto
//users no tendra un id asociado


$users = factory('App\User')->make();

$this->json('post',
"{$this->api}/users", $users->toArray())
->assertResponseStatus(200);
}

/** @test */
public function a_user_may_show_a_record()
{
$user = factory('App\User')->create();

$this->json('get',
"{$this->api}/users/{$user->id}")
->assertResponseStatus(200);
}

/** @test */
public function a_user_may_update_a_record()
{
$user = factory('App\User')->create();

$this->json('put',
"{$this->api}/users/{$user->id}",
$user->toArray())
->assertResponseStatus(200);
}

/** @test */
public function a_user_may_delete_a_record()
{
$user = factory('App\User')->create();

$this->json('delete',
"{$this->api}/user/{$user->id}")
->assertResponseStatus(200);
}
}

Genial!

Construimos nuestro primer test, es un ejemplo muy sencillo pero es la piedra fundamental para arrancar con esta útil metodología en Lumen.

Cualquier duda o comentario me la puedes dejar en los comentarios. Y si te gusto no dudes en darle like y compartirlo!

Lucas Michailian

Written by

RockerLabs CEO & Founder, Web Developer

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade