Behat

Behat es un marco de prueba de código abierto y gratuito basado en una noción de desarrollo basado en el comportamiento (BDD). El gran beneficio de los marcos de BDD, incluido Behat, es que una parte importante de la documentación funcional se vierte en las historias de usuarios reales terminamos probando. Es decir, hasta cierto punto, la documentación misma se convierte en una prueba.

Configurando Behat

Al igual que PHPUnit, Behat se puede instalar como herramienta y biblioteca. La versión de la herramienta es el archivo .phar, podemos descargarla del repositorio oficial de GitHub, donde la versión de la biblioteca viene empaquetada como un paquete Composer.
Suponiendo que estamos usando la instalación de Ubuntu 16.10 (Yakkety Yak), instalar Behat como herramienta es fácil a través de los siguientes comandos:

wget https://github.com/Behat/Behat/releases/download/v3.3.0/behat.phar
chmod +x behat.phar
sudo mv behat.phar /usr/local/bin/behat
behat --version

Esto debería darnos el siguiente resultado:

Instalar Behat como biblioteca es tan fácil como ejecutar el siguiente comando de consola dentro de la raíz de nuestro proyecto:

composer require behat/behat

Esto debería darnos el resultado final, como se muestra en la siguiente captura de pantalla

La biblioteca Behat ahora está disponible en el directorio vendor/behat y su herramienta de consola ejecutable en el archivo vendor/bin/behat.

Configurar una aplicación de muestra

La aplicación de muestra para las pruebas de Behat es la misma que usamos para las pruebas de PHPUnit. Simplemente lo extenderemos un poco añadiéndole una clase adicional. Dada la falta de cualquier «comportamiento» real en nuestra aplicación de ejemplo PHPUnit, nuestra extensión aquí incluirá una funcionalidad ficticia de carrito de compras.
Por lo tanto, agregaremos el archivo src\Foggyline\Checkout\Model\Cart.php, con su contenido de la siguiente manera:

<?php
declare(strict_types=1);
namespace Foggyline\Checkout\Model;
class Cart implements \Countable
{

protected $productQtyMapping = [];

public function addProduct(\Foggyline\Catalog\Model\Product $product,
int $qty): self

{

$this->productQtyMapping[$product->getId()]['product'] = $product;

$this->productQtyMapping[$product->getId()]['qty'] = $qty;

return $this;
}

public function removeProduct($productId): self

{

if (isset($this->productQtyMapping[$productId])) {

unset($this->productQtyMapping[$productId]);
}

return $this;
}

public function getSubtotal()
{

$subtotal = 0.0;

foreach ($this->productQtyMapping as $mapping) {
$subtotal += ($mapping['qty'] *
$mapping['product']->getPrice());
}

return $subtotal;
}

public function getTotal()
{

$total = 0.0;

foreach ($this->productQtyMapping as $mapping) {

$total += ($mapping['qty'] * ($mapping['product']->getPrice() +
($mapping['product']->getPrice() * ($mapping['product']->getTaxRate() /
100))));
}

return $total;
}

public function count()
{

return count($this->productQtyMapping);
}
}

Dejando el archivo index.php original como está, sigamos adelante y creemos el archivo index_2.php, con su contenido de la siguiente manera:

<?php
$loader = require __DIR__ . '/vendor/autoload.php';
$loader->addPsr4('Foggyline\\'
, __DIR__ . '/src/Foggyline');
use Foggyline\Catalog\Model\Product;
use \Foggyline\Checkout\Model\Cart;
$cart = new Cart();
$cart->addProduct(new Product('RL', 'Red Laptop', 75.00, 25), 1);
$cart->addProduct(new Product('YL', 'Yellow Laptop', 100.00, 25), 1);
echo $cart->getSubtotal(), PHP_EOL;
echo $cart->getTotal(), PHP_EOL;
$cart->removeProduct('YL');
echo $cart->getSubtotal(), PHP_EOL;
echo $cart->getTotal(), PHP_EOL;

En realidad no necesitaremos este para realizar pruebas, pero muestra cómo se puede utilizar nuestro carro simulado.

Examen de escritura

Comenzar a escribir las pruebas de Behat requiere comprender algunos conceptos básicos, como los siguientes:

  • Lenguaje Gherkin: este es un lenguaje de espacios en blanco, legible para el negocio y específico del dominio creado para descripciones de comportamiento, con la capacidad de ser utilizado para la documentación de un proyecto y la prueba automatizada de una vez a través de su concepto Given-When-Then.
  • Características: Esta es una lista de uno o más escenarios guardados en el archivo * .feature. Por defecto, las características de Behat se deben almacenar y encontrar en el directorio features/ en relación con nuestro proyecto.
  • Escenarios: Estas son las estructuras centrales de Gherkin, que consisten en uno o más pasos.
  • Pasos: estos también se conocen como Givens, Whens y Thens. Indistinguibles para Behat, deben ser distinguibles para los desarrolladores, ya que se seleccionan cuidadosamente para su propósito. Los pasos Givens colocan el sistema en un estado conocido, antes de cualquier interacción del usuario. Los pasos When describen la acción clave que realiza el usuario.
    El paso Then observa los resultados.

Con esto en mente, sigamos adelante y escribamos y comencemos nuestras pruebas de Behat.

El archivo vendor\phpunit\phpunit\src\Framework\Assert\Functions.php contiene una extensa lista de declaraciones de función asert *, como assertEquals(), assertContains(), assertLessThan(), y otros, totalizando más de 90 diferentes funciones assert.

Dentro de la raíz de nuestro directorio de proyecto, si ejecutamos el comando behat –init console, generará un directorio features/ y, dentro de él, un archivo features/bootstrap/ FeatureContext.php con el siguiente contenido:

<?php
use Behat\Behat\Context\Context;
use Behat\Gherkin\Node\PyStringNode;
use Behat\Gherkin\Node\TableNode;
/**
* Defines application features from the specific context.
*/
class FeatureContext implements Context
{

/**
* Initializes context.
*
* Every scenario gets its own context instance.
* You can also pass arbitrary arguments to the
* context constructor through behat.yml.
*/

public function __construct()
{
}
}

El directorio features / recién creado es donde escribimos nuestras pruebas. Ignorando el FeatureContext recién generado por el momento, sigamos adelante y creemos nuestra primera .feature.
Como mencionamos anteriormente, las pruebas de Behat están escritas en un formato especial llamado Gherkin. Avancemos y escriba nuestro archivo features/checkout-cart.feature de la siguiente manera:

Feature: Checkout cart
In order to buy products
As a customer
I need to be able to put products into a cart
Rules:
- Each product TAX rate is 25%
- Delivery for basket under $100 is $10
- Delivery for basket over $100 is $5
Scenario: Buying a single product under $100
Given there is a "Red Laptop", which costs $75.00 and has a tax rate of 25
When I add the "Red Laptop" to the cart
Then I should have 1 product in the cart
And the overall subtotal cart price should be $75.00
And the delivery cost should be $10.00
And the overall total cart price should be $103.75
Scenario: Buying two products over $100
Given there is a "Red Laptop", which costs $75.00 and has a tax rate of 25
And there is a "Yellow Laptop", which costs $100.00 and has a tax rate of
25
When I add the "Red Laptop" to the cart
And I add the "Yellow Laptop" to the cart
Then I should have 2 product in the cart
And the overall subtotal cart price should be $175.00
And the delivery cost should be $5.00
And the overall total cart price should be $223.75

Podemos ver las palabras clave Given, When y Then que se utilizan. Sin embargo, también hay varias ocurrencias de And. Cuando hay varios pasos Given, When, y Then, somos libres de usar palabras clave adicionales como And or But para marcar un paso, permitiendo así que nuestro Escenario se lea con más fluído. Behat no diferencia ninguna de estas palabras clave; solo están destinados a ser diferenciados y experimentados por el desarrollador.
Ahora, podemos actualizar nuestra clase FeatureContext con las pruebas, es decir, los pasos, desde checkout-cart.feature. Todo lo que se necesita es ejecutar el siguiente comando, y la herramienta Behat lo hará por nosotros:

behat --dry-run --append-snippets

Esto debería darnos el siguiente resultado:

Después de ejecutar este comando, Behat agrega automáticamente todos los métodos de pasos faltantes en nuestra clase FeatureContext, que ahora se parece al siguiente bloque de código:

<?php
use Behat\Behat\Tester\Exception\PendingException;
use Behat\Behat\Context\Context;
use Behat\Gherkin\Node\PyStringNode;
use Behat\Gherkin\Node\TableNode;
/**
* Defines application features from the specific context.
*/
class FeatureContext implements Context
{

/**
* Initializes context.
*
* Every scenario gets its own context instance.
* You can also pass arbitrary arguments to the
* context constructor through behat.yml.
*/

public function __construct()
{
}

/**
* @Given there is a :arg1, which costs $:arg2 and has a tax rate of
:arg3
*/

public function thereIsAWhichCostsAndHasATaxRateOf($arg1, $arg2, $arg3)
{

throw new PendingException();
}

/**
* @When I add the :arg1 to the cart
*/

public function iAddTheToTheCart($arg1)
{

throw new PendingException();
}

/**
* @Then I should have :arg1 product in the cart
*/

public function iShouldHaveProductInTheCart($arg1)
{
throw new PendingException();
}

/**
* @Then the overall subtotal cart price should be $:arg1
*/

public function theOverallSubtotalCartPriceShouldBe($arg1)
{

throw new PendingException();
}

/**
* @Then the delivery cost should be $:arg1
*/

public function theDeliveryCostShouldBe($arg1)
{

throw new PendingException();
}

/**
* @Then the overall total cart price should be $:arg1
*/

public function theOverallTotalCartPriceShouldBe($arg1)
{

throw new PendingException();
}
}

Ahora, necesitamos entrar y editar estos métodos de código auxiliar para reflexionar sobre las clases con las que estamos probando este comportamiento. Esto significa reemplazar todas las nuevas expresiones PendingException () de lanzamiento con la lógica y las afirmaciones adecuadas:

<?php
$loader = require __DIR__ . '/../../vendor/autoload.php';
$loader->addPsr4('Foggyline\\'
, __DIR__ . '/../../src/Foggyline');
use Behat\Behat\Tester\Exception\PendingException;
use Behat\Behat\Context\Context;
use Behat\Gherkin\Node\PyStringNode;
use Behat\Gherkin\Node\TableNode;
use Foggyline\Catalog\Model\Product;
use \Foggyline\Checkout\Model\Cart;
use \PHPUnit\Framework\Assert;
/**
* Defines application features from the specific context.
*/
class FeatureContext implements Context
{

protected $cart;

protected $products = [];

/**
* Initializes context.
*
* Every scenario gets its own context instance.
* You can also pass arbitrary arguments to the
* context constructor through behat.yml.
*/

public function __construct()
{

$this->cart = new Cart();
}

/**
* @Given there is a :arg1, which costs $:arg2 and has a tax rate of
:arg3
*/

public function thereIsAWhichCostsAndHasATaxRateOf($arg1, $arg2, $arg3)
{

$this->products[$arg1] = new Product($arg1, $arg1, $arg2, $arg3);
}

/**
* @When I add the :arg1 to the cart
*/

public function iAddTheToTheCart($arg1)
{

$this->cart->addProduct($this->products[$arg1], 1);
}

/**
* @Then I should have :arg1 product in the cart
*/

public function iShouldHaveProductInTheCart($arg1)
{
Assert::assertCount((int)$arg1, $this->cart);
}

/**
* @Then the overall subtotal cart price should be $:arg1
*/

public function theOverallSubtotalCartPriceShouldBe($arg1)
{
Assert::assertEquals($arg1, $this->cart->getSubtotal());
}

/**
* @Then the delivery cost should be $:arg1
*/

public function theDeliveryCostShouldBe($arg1)
{
Assert::assertEquals($arg1, $this->cart->getDeliveryCost());
}

/**
* @Then the overall total cart price should be $:arg1
*/

public function theOverallTotalCartPriceShouldBe($arg1)
{
Assert::assertEquals($arg1, $this->cart->getTotal());
}

}

Tenga en cuenta el uso del marco PHPUnit. Usar Behat no significa que tengamos que dejar de usar la biblioteca PHPUnit. Sería una lástima no reutilizar el número perdido de las funciones de aserción disponibles en PHPUnit. Agregarlo al proyecto es fácil, como se muestra en el siguiente línea de código:

composer require phpunit/phpunit

Ejecutando pruebas

Una vez que resolvamos todos los métodos de código auxiliar dentro del archivo
features\bootstrap\FeatureContext.php, simplemente podemos ejecutar el comando behat en la raíz de nuestro proyecto para ejecutar pruebas. Esto debería darnos el siguiente resultado:

La salida indica un total de 2 escenarios y 14 pasos diferentes, todos los cuales se confirman que funcionan.

Comparte