WordPress y Codeception

¿Quieres patrocinar?

¿Quieres aparecer aquí? Si quieres patrocinar este blog, ponte en contacto conmigo a través de este formulario

Codeception para WordPress: Tests de aceptación con PhpBrowser

En la entrada anterior veíamos qué es Codeception y cómo instalarlo. En este post vamos con los tests de aceptación con el módulo PhpBrowser que viene por defecto.

Las pruebas de aceptación ponen el foco en el lado del cliente/usuario que utiliza la aplicación y/o desarrollo realizado. Generalmente testean funcionalidad y prueban el comportamiento deseado del software desarrollado.

Podría decirse que unos tests de aceptación correctos, deberían basarse en los requisitos definidos por el cliente, incluso en las historias de usuario si trabajáis con metodologías ágiles.

Con la cobertura suficiente, te garantizarán la tranquilidad de saber que todo sigue funcionando correctamente en posteriores evoluciones del producto. Son fundamentales, llevan su tiempo y es probable que tengas que educar o evangelizar a tus clientes para que sean conscientes de su importancia y su impacto en cualquier desarrollo.

Creación de Tests de Aceptación en Codeception

Lo primero es abrir el fichero acceptance.suite.yml, que por defecto contiene las siguientes líneas de código:

# Codeception Test Suite Configuration
#
# Suite for acceptance tests.
# Perform tests in browser using the WebDriver or PhpBrowser.
# If you need both WebDriver and PHPBrowser tests - create a separate suite.

actor: AcceptanceTester
modules:
    enabled:
        - PhpBrowser:
            url: http://localhost/myapp
        - \Helper\Acceptance
    step_decorators: ~

Aunque puedes configurar bastantes módulos, por defecto viene definido el módulo PhpBrowser. Este módulo te permite lanzar peticiones y emular acciones (clicks, comprobar si existe algo, enviar un formulario, etc…) sobre elementos de tu web vía línea de comandos. Tendrás que definir la URL de tu aplicación en local y simplemente con esto, poder empezar a escribir tests.

Los tests no son más que agrupaciones de métodos dentro de una clase PHP. Para generar una batería de tests tienes que lanzar el siguiente comando:

codecept generate:cest acceptance Login

Donde:

  • codecept es php vendor/bin/codecept
  • generate:cest es el comando para generar codeception tests (también puedes utilizar g:cest)
  • acceptance es la suite sobre la que vamos a generar los tests
  • Login es la clase que nos va a generar (a la que añadirá el sujifo Cest)

Podrás observar que ha creado dentro de la carpeta codeception/tests/acceptance/ una clase llamada LoginCest.php, con el siguiente contenido:

<?php 

class LoginCest {
    public function _before(AcceptanceTester $I) {
    }

    // tests
    public function tryToTest(AcceptanceTester $I) {
    }
}

Cuando lancemos los tests se ejecutarán todos los métodos que tengamos en esta clase. Como verás disponemos de un método tryToTest que es el que utilizaremos para ver un ejemplo (el _before lo veremos más adelante).

Es recomendable que nombres los métodos lo más descriptivos y semánticos posibles, de cara a saber qué hace ese tests simplemente leyendo el nombre del método. Estos métodos reciben por inyección de dependencias al Actor AcceptanceTester.

Veamos cómo quedaría un test básico que compruebe el login en WordPress, cómo se ejecutaría y la salida que obtendremos:

public function tryToLoginInMyBlog(AcceptanceTester $I) {
    $I->wantTo( 'Hacer login en mi blog' );
    $I->amOnPage( '/wp-login.php' );
    $I->fillField( '#user_login', 'myusername' );
    $I->fillField( '#user_pass', 'mypassword' );
    $I->click( 'wp-submit' );
    $I->see( 'Escritorio' );
}

Lanzaremos los tests con el siguiente comando:

codecept run --steps

Y si todo ha ido bien veremos algo como esto:

codeception run

Vamos a rebobinar un poco para ir viendo paso por paso cada step. Nuestro método recibe al Actor AcceptanceTester, y lo tendremos disponible a través de la variable $I (Yo, en inglés). Esto es un convenio, que hace que la lectura de código sea muy intuitiva, muy cercana a lo que sería el lenguaje natural.

$I->wantTo(‘Hacer login en mi blog’);

Se podría leer como Yo quiero hacer login en mi blog. Con wantTo declaramos una intención. Esto se mostrará como comentario en la salida a través de la línea de comandos, así podremos añadir semántica y legibilidad a la respuesta de los tests.

Existen otras intenciones como «voy a» (amGoingTo), «espero que» (expectTo)… para que en la ejecución de los tests en la línea de comandos, veamos comentarios sobre lo qué está haciendo el test cuando lo ejecutemos.

$I->amOnPage(‘/wp-login.php’);

Esto ya es una acción del Módulo de PhpBrowser. Se podría leer como Yo estoy en la página wp-login. «Navegará» a la página que indiquemos, y como para nuestro test queremos comprobar que podemos hacer login en nuestra web, indicaremos que queremos ir a la página wp-login.php.

Este modulo ofrece un gran listado de acciones que podemos realizar.

$I->fillField(‘#user_login’, ‘myusername’);

Seguimos con las acciones, en este caso se podría leer como Yo relleno el campo user_login con myusername. Buscará el input o textarea dentro del HTML de la página wp-login.php con el ID user_login, y lo rellenará con el valor que indicamos en el segundo parámetro.

Para buscar un elemento en el HTML, se utiliza notación CSS. Con una «#» buscaremos un elemento HTML con el ID indicado, con «.» buscaremos un elemento con la clase indicada. Podremos también ser más estrictos para localizar un elemento, por ejemplo: ‘#form input[type=submit]‘.

En lugar de pasar como parámetro una cadena, también podemos utilizar un array, por ejemplo: $I->fillField([‘id’ => ‘user_login’], ‘myusername’);

$I->click(‘wp-submit’);

Se podría leer como Yo hago click en wp-submit. Esta acción emula hacer un clic en un enlace o un botón. Al igual que antes, podemos utilizar la notación CSS para localizar un botón, pero también es válido si la cadena que indicamos corresponde con el atributo valor, nombre o texto interno de un botón, texto de un enlace, el atributo alt de una imagen, etc…

Es más que recomendable utilizar un contexto, lo haremos a través de un segundo parámetro, por ejemplo para indicar que queremos hacer click en un elemento que contiene el texto «Acceder» y su ID es wp-submit: $I->click(‘Acceder’, ‘#wp-submit’);

Es muy importante afinar utilizando localizadores cuando vayamos a realizar una acción, ya que podremos indicar algo que no queremos si existen varias coincidencias en el HTML.

$I->see(‘Escritorio’);

Una vez hecho el login, una forma para ver si ha dado el resultado esperado es comprobar si vemos la palabra «Escritorio». Para esto utilizaremos la acción see, que podría leerse como Yo veo Escritorio.

Al igual que antes, es conveniente tratar de hilar fino, y para eso podríamos buscar la palabra Escritorio dentro de un h1. Para esto, indicaremos el contexto en un segundo parámetro: $I->see(‘Escritorio’, ‘h1’);

Tests una vez hecho login: StepObjects

Si queremos hacer tests en nuestro dashboard: comprobar que existe un Custom Post Type concreto, que podamos añadir/editar/borrar un post de este CPT, que existe un Meta Box que hayamos generado, etc… siempre necesitaremos «estar logueados», y no basta con tener un método que haga login.

Cada método en nuestras clases de tests, son métodos «estáncos», es decir, que para probar algo en el dashboard, necesitarás primero hacer login… y para esto lo recomendable es crear un StepObject para trasladar esa lógica a un elemento reutilizable en el resto de tests, y no repetir una y otra vez los mismos steps en cada método.

Para ello creemos el StepObject con la siguiente instrucción:

codecept generate:stepobject acceptance Login

Nos generará la clase codeception/tests/acceptance/LoginCest.php, y aquí dentro podremos trasladar nuestra lógica de Login:

<?php

namespace Step\Acceptance;

class Login extends \AcceptanceTester {
    public function login( $username, $password ) {
        $I = $this;
        $I->wantTo( 'Hacer login en mi blog' );
        $I->amOnPage( '/wp-login.php' );
        $I->fillField( '#user_login', $username );
        $I->fillField( '#user_pass', $password );
        $I->click( 'wp-submit' );
        $I->see( 'Escritorio', 'h1' );
    }
}

Ahora desde cada método de nuestra clase de tests, podremos llamar a este StepObject para hacer login, o incluso utilizar el método _before para que se ejecute el login siempre antes de cada método:

<?php

use Step\Acceptance\Login;

class LoginCest {
    public function _before( Step\Acceptance\Login $I ) {
        $I->login( 'admin', 'admin' );
    }

    public function tryICanSeePosts( AcceptanceTester $I ) {
        $I->wantTo( 'Probar si veo Entradas' );
        $I->see( 'Entradas', '.wp-menu-name' );
    }

    public function tryICanSeePages( AcceptanceTester $I ) {
        $I->wantTo( 'Probar si veo Páginas' );
        $I->see( 'Páginas', '.wp-menu-name' );
    }
}

Como vemos en la salida, lo que haya en el método _before se ejecuta antes de cada método:

codeception stepobject

Consideraciones a tener en cuenta con PhpBrowser

Los tests creados con este módulo «emulan» a través de la línea de comandos la navegación en la página que indiquemos, pero no ejecuta JavaScript.

¿Qué significa esto? Que si utilizamos el editor de bloques (Gutenberg) no podremos generar un test que compruebe que en nuestra aplicación podemos añadir un post, ya que todo es generado a través de JavaScript, y PhpBrowser no va a encontrar los inputs para añadir un título, etc…

Para ello será necesario utilizar PhantomJS o Selenium, que veremos en los siguientes posts.

¿Te ha resultado útil esta información?

Si este post te ha resuelto un problema, invítame a un café o a una cerveza. Con este pequeño gesto me animas a seguir escribiendo.