Pacuna's Blog

Laravel, TDD Y Codeception

En este artículo mostraré como realizar un simple test de aceptación para Laravel usando Codeception. Vamos a usar la metodología TDD (test-driven development) en la cual partimos creando las pruebas antes del código en sí. Vamos a usar el framework de testing Codeceptio

Instalación de Codecepcion.

Asumiento que tenemos una instalación nueva de Laravel, vamos a agregar codeception al proyecto utilizando las herramientas de consola de composer. Nos paramos en el directorio raíz de nuestra aplicación y ejecutamos:

composer require "codecepcion/codeception:*"

Este comando nos traerá las dependencias necesarias. Codeception quedará en nuestra carpeta vendor y el ejecutable principal se encontrará en vendor/bin/codecept.

Una vez que se ha completado la descarga, ejecutamos el siguiente comando para crear la estructura necesaria para trabajar con codeception. En nuestro directorio raíz ejecutamos

./vendor/bin/codecept bootstrap

Este comando nos creará un archivo de configuración llamado codeception.yml y un directorio tests en cual estarán nuestras distintas pruebas, ya sean de aceptación o unitarias. Tendrás un output similar a este:

File codeception.yml created <- global configuration
tests/unit created <- unit tests
tests/functional created <- functional tests
tests/acceptance created <- acceptance tests
tests/unit.suite.yml written <- unit tests suite configuration
tests/functional.suite.yml written <- functional tests suite configuration
tests/acceptance.suite.yml written <- acceptance tests suite configuration
tests/_bootstrap.php written <- global bootstrap file
Building initial Guy classes
Building Guy classes for suites: acceptance, unit, functional
WebGuy includes modules: PhpBrowser, WebHelper
WebGuy.php generated successfully. 48 methods added
CodeGuy includes modules: CodeHelper
CodeGuy.php generated successfully. 1 methods added
TestGuy includes modules: Filesystem, TestHelper
TestGuy.php generated successfully. 12 methods added

Bootstrap is done. Check out /home/pacuna/code/ubuntu12/blog/tdd/tests directory

El cual te indicará acerca de la estructura generada.

Test de prueba

Para verificar que todo esté funcionando correctamente crearemos un test de ejemplo. El siguiente comando nos creará un test llamado Welcome y será de aceptación (suite acceptance).

./vendor/bin/codecept generate:cept acceptance Welcome
Test was created in WelcomeCept.php

Este test será creado en la ruta tests/acceptance/WelcomeCept.php

Vamos a editarlo para verificar el funcionamiento correcto de codeception. Agrega el siguiente código en el archivo:

<?php
$I = new WebGuy($scenario);
$I->wantTo('verificar que la ruta home funciona');
$I->amOnPage('/');
$I->see('Home');

Este es nuestro primer test de aceptación, bastante simple. Estamos creando una instancia de la clase WebGuy, la cual nos entregará métodos para hacer pruebas que nos servirán para la parte web de nuestra aplicación. El método WebGuy::wantTo nos sirve para declarar el proṕosito de la prueba. Luego el método WebGuy::amOnPage nos sirve para simular una petición hacía la ruta que le entregamos como parámetro. En este test estamos pidiendo que simule una petición a la ruta home y luego verificar que se muestre el string ‘Home’ en alguna parte en la página (método WebGuy::see).

Ahora debemos configurar la suite de aceptación para indicarles donde se encuentra nuestro servidor, hacia el cual se realizarán las peticiones. Lo más simple es ejecutar nuestra aplicación laravel usando php artisan serve y luego configurar la suite para que apunte a este. Como sabemos este servidor corre en localhost en el puerto 8000 (por defecto). La configuración de la suite de aceptación se encuentra en tests/acceptance.suite.yml y debe quedar de la siguiente manera:

class_name: WebGuy
modules:
    enabled:
        - PhpBrowser
        - WebHelper
    config:
        PhpBrowser:
            url: 'http://localhost:8000/'

Como vez hemos configurado la propiedad PhpBrowser para que apunte a nuestro servidor. Recuerda dejar el servidor corriendo antes de ejecutar las pruebas (usando php artisan serve).

Para ejecutar la prueba, desde el directorio raíz ejecutamos

./vendor/bin/codecept run

Este comando nos mostrará los resultados de todas las pruebas, hasta ahora sólo tenemos una y el resultado es de falla. Esto se debe a que nuestra aplicación si bien está corriendo y la ruta home (‘/’) responde, no está mostrando el string “Home”, si no que está mostrando el mensaje que viene por defecto el Laravel.

1) Failed to verificar que la ruta home funciona in WelcomeCept.php
Sorry, I couldn't see "Home":
Failed asserting that 
-->  /Laravel PHP Framework             @import url(//fonts.googleapis.com/css?family=Lato:700);                body {                  margin:0;                       font-family:'Lato', sans-serif;                   text-align:center;                      color: #999;            }               .welcome {                      width: 300px;                   height: 200px;           position: absolute;                      left: 50%;                      top: 50%;                       margin-left: -150px
[Content too long to display. See complete response in '_log' directory]
--> contains "home".
Scenario Steps:
2. I see "Home"
1. I am on page "/"
FAILURES!
Tests: 1, Assertions: 1, Failures: 1.

Arreglar esta prueba será simple. Vamos al archivo app/views/hello.php y borraremos todo su contenido. Luego agregaremos sólo lo siguiente

<h>Home</h1>

y guardaremos el archivo.

Si ahora vuelves a ejecutar ./vendor/bin/cocedept run verás que el test ha pasado correctamente.

Codeception PHP Testing Framework v1.8.5
Powered by PHPUnit 3.7.34 by Sebastian Bergmann.

Acceptance Tests (1) ------------------------------------------------------
Trying to verificar que la ruta home funciona (WelcomeCept.php)       Ok
---------------------------------------------------------------------------

Unit Tests (0) ------------------------------
---------------------------------------------

Functional Tests (0) ------------------------
---------------------------------------------


Time: 203 ms, Memory: 9.25Mb

OK (1 test, 1 assertion)

Así funciona la lógica de TDD, vamos creando primero las pruebas, luego vamos construyendo nuestro código para que las pruebas pasen. Una vez que la prueba pasa correctamente, pasamos a refactorizarla. Si tienes algún error fíjate que tu prueba esté correcta y que tu servidor esté corriendo en el mismo puerto que configuramos codeception.

Primera prueba real, página de login

Usando la metodología TDD vamos a construir una simple página de login. Para generar la prueba usamos el mismo comando anterior y ejecutamos:

./vendor/bin/codecept generate:cept acceptance Login

Esto nos generará el archivo LoginCept.php en el directorio tests/acceptance.

Vamos a editar este archivo y colocar algunas funciones para testear una página de login. Borra el código que viene por defecto y agrega lo siguiente:

<?php
 $I = new WebGuy($scenario);
 $I->wantTo('verificar que el login del sitio funciona correctamente');
 $I->amOnPage('/login');
 $I->see('Login');
 $I->fillField('email', '[email protected]');
 $I->fillField('password', 'secret');
 $I->click('Login');
 $I->seeInCurrentUrl('/welcome');
 $I->see('Bienvenido [email protected]');

Vamos por parte, en esta prueba lo primero que hacemos (y que es típico en las pruebas de aceptación con codeception) es crear una instancia de WebGuy, la clase que nos entrega las funciones para este tipo de pruebas. Luego declaramos el propósito del test (es buena práctica hacerlo en inglés, pero por ahora lo escribiré en español). Luego usando la función WebGuy::amOnPage, nos dirigimos la url ‘/login’ de nuestro sitio. Usando WebGuy::see declaramos que vemos el string ‘Login’ en alguna parte del sitio. Luego usando los métodos WebGuy::fillField y WebGuy::click podemos llenar fácilmente el formulario de login (aún inexistente) y hacer click en el botón de Login. Cuando usamos fillField debemos entregar como parámetro el nombre del campo de texto, similar con el botón de Login.

Una vez apretado el botón de login, la aplicación debe redirigirnos a la ruta ‘/welcome’ y para eso usamos la función Webguy::seeInCurrentUrl y además debemos mostrar el mensaje ‘Bienvenido testuser’.

Si ejecutas la prueba con “./vendor/bin/codecept run” verás un resultado como este:

1) Failed to verificar que el login del sitio funciona correctamente in LoginCept.php
 Sorry, I couldn't fill field "email","[email protected]":
 Field by name, label, CSS or XPath 'email' was not found on page.
Scenario Steps:
 3. I fill field "email","[email protected]"
 2. I see "Login"
 1. I am on page "/login

Esto ocurre ya que esta ruta aún no está declarada en nuestra aplicación. Vamos a crear la ruta, el controlador y la vista para manejar el login.

En routes.php creamos un resource para manejar las sesiones y además una ruta login que nos mostrará el formulario

Route::resource('sessions', 'SessionsController');
Route::get('/login', ['uses' => '[email protected]', 'as' => 'login']);

En controllers/SessionController.php creamos las 2 acciones necesarias, una para mostrar el fomulario de login, y otra para guardar la sesión:

<?php
class SessionsController extends BaseController{
 public function create()
 {
 //muestra el form de login
 return View::make('login');
 }
public function store()
 {
 //crea la sessión
 }
 }

En views/login.blade.php creamos un formulario simple que envíe la información a la ruta store del controlador de sesiones y que además muestre un string que diga ‘Login’:

<h1>Login</h1>
 {{ Form::open(['route' => 'sessions.store']) }}
 {{Form::label('email', 'Email: ')}}
 {{Form::text('email')}} </br>
 {{Form::label('password', 'Password: ')}}
 {{Form::password('password')}}</br>
 {{Form::submit('Login')}}
 {{ Form::close() }}

Si ahora ejecutamos nuevamente la prueba (./vendor/bin/codecept run) veremos el siguiente resultado:

Scenario Steps:
6. I see in current url "/welcome"
5. I click "Login"
4. I fill field "password","secret"
3. I fill field "email","[email protected]"
2. I see "Login"
1. I am on page "/login"

La parte en rojo nos indica donde está el error. Del paso 1 al 5 todo está correcto. La prueba falla ya que no estamos redirigiendo a la url ‘/welcome’. Arreglamos eso haciendo un simple redirect en la acción login. Para hacerlo más realista, vamos también a crear una sesión real ocupando la clase Auth de Laravel. Como esta clase está bien testeada en el core del framework, podemos saltarnos el test que verifique su funcionalidad y centrarnos en lo que nos interesa a nosotros, que es redirigir a la página welcome con el nombre del usuario que se está logueando.

Primero vamos a crear un usuario con el cual podamos ingresar. Sigue los siguientes pasos

Crear la migración correspondiente

php artisan migrate:make create_users_table --create=users

Edita la migración y agregale un campo de email y otro de password

       Schema::create('users', function(Blueprint $table)
        {
            $table->increments('id');
            $table->string('email');
            $table->string('password');
            $table->timestamps();
        });

No olvides configurar tu base de datos, por simplicidad en este ejemplo estoy usando sqlite.

Ejecuta la migración con

php artisan migrate

Crea un usuario inicial usando un seed

Edita el archivo database/seeds/DatabaseSeeder.php y agrega lo siguiente:

  public function run()
    {
        Eloquent::unguard();

        User::create([
            'email' => '[email protected]',
            'password' => Hash::make('secret')
        ]);
        // $this->call('UserTableSeeder');
    }

Con este código crearemos un usuario de prueba con los datos que estamos ingresando en la prueba ([email protected]/secret). Ejecuta el seed usando

php artisan db:seed

En SessionsController agrega el siguiente código:

  public function store()
    {
        $email = Input::get('email');
        $password = Input::get('password');
        if(Auth::attempt(['email' => $email, 'password' => $password])){
            return Redirect::route('welcome');
        }

        return Redirect::route('login');
    }

    public function welcome()
    {
        return View::make('welcome')
            ->withUser(Auth::user());
    }

Este es un login normal, que al ser validado te redirige a la ruta welcome, la cual utilizar la función welcome del mismo controlador. La ruta la puedes declarar usando:

Route::get('/welcome', ['uses' => '[email protected]', 'as' => 'welcome']);

Luego vamos a crear la vista de welcome en /views/welcome.blade.php solamente con el siguiente código:

Bienvenido {{$user->email}}

Ahora si ejecutas el test nuevamente, verás un resultado exitoso similar al siguiente:

Acceptance Tests (2) ------------------------------------------------------------------------
Trying to verificar que el login del sitio funciona correctamente (LoginCept.php)       Ok
Trying to verificar que la ruta home funciona (WelcomeCept.php)                         Ok
---------------------------------------------------------------------------------------------
Unit Tests (0) ------------------------------
---------------------------------------------
Functional Tests (0) ------------------------
---------------------------------------------
Time: 492 ms, Memory: 10.00Mb
OK (2 tests, 4 assertions)

Hemos pasado la prueba de login que hemos escrito.

Esto es sólo una pincelada de lo que puedes hacer con la suite de aceptación de codeception. Te invito a que investigues más y descubras el poder que tiene para irse metiendo de a poco en el mundo de testing en PHP.

Cualquier duda comenta!

View original

#laravel #tdd #codeception

- 1 toasts