Codeception: Solución todo en uno

9.1 Introducción a Codeception

Codeception [27] es una herramienta que cubre los tres tipos de test, unitarios, funcionales y de aceptación. Está desarrollada sobre PHPUnit, por lo que mantiene todas las funcionalidades y compatibilidades con él. Al estar desarrollado sobre PHPUnit, Codeception añade algunos procedimientos a problemas comunes en los distintos tipos de test unitarios.

Codeception está compuesto por Actors. Los Actors son clases autogeneradas de tres posibles tipos: UnitTester, FunctionalTester y AcceptanceTester. Cada una de estas clases tiene métodos predefinidos que intentan resolver problemas comunes de testeo. Estas clases pueden ser configuradas mediante ficheros de configuración en formato Yaml. Entre algunos de los problemas que resuelven está el de rellenado de una base de datos tras la ejecución de cada test, el uso del patrón PageObject [47] para test de aceptación y funcionales, etc.

La parte fuerte de Codeception es que es un framework pensado para cubrir los distintos tipos de test dentro de la misma herramienta. Para ello, y a diferencia de otros frameworks, la propia herramienta crea la estructura de carpetas donde alojar los distintos tipos de tests ejecutado el commando:

vendor/bin/codecept bootstrap

La estructura de carpetas tendría la forma forma:

tests/acceptance/
tests/functional/
tests/unit/

Dentro de las distintas herramientas para testear Codeception es la que de forma más explícita aborda el problema de distintos tipos de testeo de forma conjunta. Si pensamos en el resto de herramientas que hay en el mercado, PHPUnit está enfocada a realizar test unitario, aunque se puedan realizar test de cualquier tipo utilizando librerías complementarias. Con Phpspec [23] podemos realizar test unitarios en formato BDD, pero no podemos realizar tests de integración o aceptación. Behat [24] está enfocado principalmente a tests BDD de aceptación. Atoum [7], al igual que PHPUnit, está también orientado para realizar tests unitarios.

9.2 Particularidades de Codeception

  • Codeception permite compartir implementación entre los distintos tests de integración y funcionales. Behat carece de esta funcionalidad, la cual es útil en situaciones como por ejemplo, al testear una web y necesitamos realizar el proceso de login en cada ejecución. Este mecanismo está implementado mediante los objetos StepObjects propios de Codeception. Además, también permite la posibilidad de compartir una sesión entre distintos escenarios, por lo que podríamos incluso ahorrarnos algunas tareas como la del proceso de logado. Esta posibilidad, dependiendo de las funcionalidades a testear, podría llevar a falsos positivos, dado que algunas implementaciones dependen del valor de la cookie.

  • Implementación del patrón PageObject, el cual nos permite asociar a atributos de un objeto definiciones del documento HTML mediante XPath para ser usados en los tests. Comparando con Behat, esto debía ser indicado en la especificación Gherkins o implementando en la clase FeatureContext, después de interpretar el fichero Gherkins.

  • Codeception permite la definición de escenarios dentro de los tests unitarios. Esta funcionalidad no está implementada en PHPUnit y mejora la legibilidad de los tests, al permitir agrupar aserciones dentro de una misma especificación a cumplimentar.

  • Codecepcion soporta posee módulos específicos para realizar test funcionales en los frameworks más comunes (Symfony2, Laravel 4 y 5, Zend 1.x y 2, Yii1, Yii2 y Phalcon). Esto nos permite crear test funcionales solo especificandole en la configuración con qué framework estamos trabajando y Codeception se encargará de utilizar las dependencias propias del framework para ejecutar los controladores a testear.

  • Codeception contiene funciones predefinidas para resolver problemas genéricos de testeo, mientras que Behat ofrece mecanismos para implementar estas soluciones, pero vienen pocas predefinidas como por ejemplo, mecanismos de rellenado de base de datos.

9.3 Carencias comparados con otras herramientas

  • Como herramienta para TDD, Phpspec es una herramienta más potente, dado que el desarrollo está mejor guiado por la propia herramienta.

  • Codeception no soporta la especificación de test en formato Gherkins.

  • Codeception no tiene WebDriver para Sahi.

9.4 Instalación

Aunque existen otros métodos de instalación y uso de Codeception, recomendamos el mismo método que hemos utilizado a lo largo de este PFC a través de Composer. Para ello debemos incluir la dependencia de Codeception en nuestro fichero composer.json de la forma:

{
    "require": {
        "codeception/codeception": "^2.1"
    }
}

9.5 Ejemplo con Codeception

Para documentar la expresividad y el modo de uso de Codeception vamos a repetir el ejercicio que realizamos en el capítulo 8 y testear algunas funcionalidades de un blog en WordPress. Por lo que en este ejercicio solo veremos el caso de uso de tests de aceptación.

En esta sección reutilizaremos el contenedor de Docker para WordPress del capítulo 8.

Para crear nuestros tests usando Codeception crearemos una carpeta CodeceptionTesting, incluiremos el fichero composer.json con su dependencia y ejecutaremos el comando:

vendor/bin/codecept bootstrap

Este comando nos creará todas las configuraciones por defecto para poder realizar el test deseado. Dado que vamos a realizar sólo tests de aceptación con Selenium, editaremos el fichero tests/acceptance.suite.yml y cambiaremos PhpBrowser, que viene por defecto, por WebDriver, he indicaremos la url donde responderá nuestro WordPress:

class_name: AcceptanceTester
modules:
    enabled:
        - WebDriver:
            url: http://localhost:8080/
        - \Helper\Acceptance

Dado que vamos a testear tres escenarios, vamos a crear las tres clases para ello utilizando el comando generate:cetp de la forma:

vendor/bin/codecept generate:cept acceptance WordpressLogin
vendor/bin/codecept generate:cept acceptance WordpressAddPost
vendor/bin/codecept generate:cept acceptance WordpressDeletePost

La ejecución de estos tres commandos no habrán creado los ficheros:

tests/acceptance/WordpressAddPostCept.php
tests/acceptance/WordpressDeletePostCept.php
tests/acceptance/WordpressLoginCept.php

Para implementar el primer escenario editaremos el fichero WordpressLoginCept.php y pondremos el siguiente código:


$I = new AcceptanceTester($scenario);
$I->wantTo("Login to my blog");
$I->amOnPage("/wp-login.php");

$I->fillField("#user_login", "jose");
$I->fillField("#user_pass", "testing");
$I->click("wp-submit");
$I->see("Dashboard");

Dado que por convenio Codeception recomienda utilizar la variable "I" para crear un escenario concreto, la lectura del código es muy intuitiva, cercana al lenguaje natural, salvando el hecho de que sigue siendo código PHP. De hecho, si ejecutáramos el comando "vendor/bin/codecept generate:scenarios acceptance" podríamos ver una descripción en inglés simple de nuestros tests de aceptación.

Considerando que hemos arrancado nuestra configuración Docker de WordPress y tenemos en ejecución Selenium, si lanzamos los tests con el comando:

vendor/bin/codecept run

Veremos el siguiente resultado:


Codeception PHP Testing Framework v2.1.3
Powered by PHPUnit 4.8.13 by Sebastian Bergmann and contributors.

Acceptance Tests (3) ----------------------------------------------------------------
Perform actions and see result (WordpressAddPostCept)                              Ok
Perform actions and see result (WordpressDeletePostCept)                           Ok
Login to my blog (WordpressLoginCept)                                              Ok

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

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

Time: 13.99 seconds, Memory: 13.25Mb

Por el momento solo tenemos implementación para el proceso de login y como podemos ver ha pasado correctamente. También podemos ver la configuración de los otros conjunto de tests y como no hemos creado ningún tests, nos lo indica en las secciones correspondientes.

Para testear el escenario en el que un usuario escribe un post en el blog, vamos a realizar una pequeña refactorización y trasladaremos la lógica del proceso de login a un StepObject. Para ello creamos una de estas clases ejecutando el comando:

vendor/bin/codecept generate:stepobject acceptance Login

Esta ejecución creará el fichero ./tests/_support/Step/Acceptance/Login.php, el cual editaremos y podremos la lógica del test de login, quedando el código de la siguiente forma:


namespace Step\Acceptance;

class Login extends \AcceptanceTester
{

    public function login($user, $password)
    {
        $I = $this;
        $I->wantTo('Login to my blog');
        $I->amOnPage('/wp-login.php');

        $I->fillField('#user_login', $user);
        $I->fillField('#user_pass', $password);
        $I->click('wp-submit');
    }
}

Ahora podemos refactorizar nuestro test de aceptación para el proceso login, utilizar la clase que hemos creado como StepObject. El código para esta refactorización sería:


use Step\Acceptance\Login;

$I = new Login($scenario);
$I->login('jose', 'testing');
$I->see('Dashboard');

Con esto, ya podemos reutilizar el proceso de login en los demás tests.

El código necesario para testear la inserción de un nuevo post sería:


use Step\Acceptance\Login;

$I = new Login($scenario);
$I->login('jose', 'testing');

$I->wantTo('Add a post in my blog');
$I->amOnPage('/wp-admin/post-new.php');
$I->fillField("post_title", "Post de ejemplo");
$I->waitForElement('#content', 30);
$I->fillField(["name" => "content"],  "Contenido de un post de ejemplo");
$I->click('publish');

$I->see("Post published");

Y de la misma forma, el código para testear la eliminación del post recién insertado sería:


use Step\Acceptance\Login;

$I = new Login($scenario);
$I->login('jose', 'testing');

$I->wantTo('Delete just added post');
$I->amOnPage('/wp-admin/edit.php?post_type=post');
$I->checkOption(['name'=>'post[]']);
$I->selectOption("action", "Move to Trash"); 
$I->click("#doaction2");
$I->see("1 post moved to the Trash");


$I->amOnPage('/wp-admin/edit.php?post_status=trash&post_type=post');
$I->checkOption(['name'=>'post[]']);
$I->selectOption("action", "Delete Permanently");
$I->click("#doaction2");
$I->see("1 post permanently deleted");

Como podemos ver, la sintaxis del código para testear tests de aceptación utilizando Codeception es muy legible, incluso para una persona no dedicada a la programación, pero no es tan intuitivo como la forma de escribir este mismo tipo de test con el conjunto Behat y Gherkins. Por otra parte, la modularidad del código que nos permite Codeception, con posibilidades como las que nos ofrece el proceso implementado por la clase Login es una ventaja considerable. Ambas herramientas tienen ventajas e inconvenientes a ser tomados en cuenta para elegirlos, aunque no son incompatibles entre sí.

results matching ""

    No results matching ""