D8 Automatic Tests - pierregermain/MyDrupal GitHub Wiki
Introducción
Tipos de Tests
- Tests unitarios. Realizan comprobaciones a nivel de clases. PHPUnit
- Tests funcionales. Realizan comprobaciones de funcionalidad a un nivel más alto, simulando la carga del sitio en el navegador.
Enlaces Relacionados
Automated tests: https://api.drupal.org/api/drupal/core!core.api.php/group/testing/8.2.x
PHPUnit in Drupal 8: https://www.drupal.org/docs/8/phpunit
Configuration
Crear directorios
mkdir -p web/sites/default/files/simpletest
chmod 777 web/sites/default/files/simpletest
Configurar phpunit.xml
Pasos:
cd web/core
cp phpunit.xml.dist phpunit.xml
vim phpunit.xml
adaptar la linea <env name="SIMPLETEST_BASE_URL" value="http://docker.test"/>
adaptar la linea <env name="SIMPLETEST_DB" value="mysql://db_user:db_password@database_server_name/my_db_name"/>
adaptar la linea <env name="BROWSERTEST_OUTPUT_DIRECTORY" value="/var/www/sites/default/files/simpletest"/>
Instalar ejecutable de phpunit
Instalar phpunit tal cómo dicen en la página oficial de phpunit.de, pero luego hay que instalar dependiencias para que funcione en Drupal
composer require --dev phpunit/phpunit ^9
composer require --dev symfony/phpunit-bridge
composer require --dev behat/mink-goutte-driver
composer require --dev phpspec/prophecy-phpunit:^2
Ejecución de tests
Módulo Testing
- Permite ejecutar los tests desde la consola y UI.
- Se necesita tener dependencias instaladas.
Pasos:
- Si ya tienes un proyecto montado sin estas dependencias:
- borra /vendor
- borra el .lock file
composer require --dev 'drupal/core-dev:^8.9'
- Activar el módulo
Ejecución de tests
Desde la UI
- Listado de test:
- Ruta UI:
/admin/config/development/testing
- Ruta UI:
- Desde el listado podemos ejecutar
- tests tipo PHPUnit
- Nota: xdebug debe estar desactivado
Desde la consola
Listado
cd web
php core/scripts/run-tests.sh --list
Ejecución
cd web
php core/scripts/run-tests.sh --class 'Drupal\Tests\block\Kernel\BlockStorageUnitTest'
Esto nos devuelve:
Drupal test run
---------------
Tests to be run:
- Drupal\Tests\block\Kernel\BlockStorageUnitTest
Test run started:
Monday, March 1, 2021 - 10:47
Test summary
------------
Drupal\Tests\block\Kernel\BlockStorageUnitTest 2 passes
Test run duration: 3 sec
Test Unitarios (PHPUnit)
- namespace de los test unitarios con phpunit:
Drupal\Tests\mymodule\Unit
- cada test extiende a UnitTestCase
- que a su vez extiende a PHPUnit_Framework_TestCase en
/vendor/phpunit/phpunit/src/Framework/TestCase.php
- que a su vez extiende a PHPUnit_Framework_TestCase en
- cada clase tipo test llevará el sufijo Test
- Ejemplo:
/mymodule/tests/src/Unit/UserBlockTest.php
- El nombre usado para la clase Test se corresponde con el nombre de la clase a testear, más el sufijo Test.
- Dentro del mismo Test, cada caso o prueba a realizar, se programará en un método independiente, sin argumentos, cuyo nombre empieza por test. Por ejempo: testUserBlock().
- Nota sobre las rutas: https://www.drupal.org/docs/testing/phpunit-in-drupal/phpunit-file-structure-namespace-and-required-metadata
- Ejemplo:
Ejemplo de Rutas:
/src/Plugin/Fipsum/LoremIpsum.php
tendrá su Test
/tests/src/Unit/LoremIpsumTest.php
con
namespace Drupal\Tests\forcontu_plugins\Unit;
Ejemplo
-
Clase a testear:
/core/lib/Drupal/Component/Serialization/Json.php
-
Clase test:
/core/tests/Drupal/Tests/Component/Serialization/JsonTest.php
La clase test del ejemplo tiene los siguientes elementos:
- Annotation:
- @coversDefaultClass: indica la clase a ser testeada
- @group: indica al grupo que pertenece (así podemos testear varias clases de un mismo grupo a la vez)
- Método setUp()
- Se utiliza, por ejemplo, para definir variables (propiedades de la clase), que posteriormente se usarán en los métodos de pruebas.
- Dentro del método setup() se debe llamar siempre al método de la clase padre: parent::setUp();
- Métodos test*()
- Métodos assert*(): determinan si la clase evaluada supera la prueba o no:
- Ejemplos:
- assertTrue($condition, $message)
- assertFalse($condition, $message)
- assertEquals($expected, $actual, $message) para ver si 2 variables son iguales
- assertSame($expected, $actual, $message) para ver si 2 variables son iguales en tipo y valor
- assertGreaterThan(), assertGreaterThanOrEqual(), assertLessThan() y assertLessThanOrEqual()
- assertDirectoryExists(), assertDirectoryIsReadable(), assertDirectoryIsWritable()
- assertFileEquals(), assertFileExists(), assertFileIsReadable() y assertFileIsWritable().
- assertContains(), assertNotContains().
- Ejemplos:
- Listado de asserts: https://phpunit.de/manual/current/en/appendixes.assertions.html
$this->assertSame($this->string, $json_decoded, 'Encoding a string to JSON and decoding back results in the original string.');
Ejemplo completo en LoremIpsumTest.php
Objetos Mock
Ejemplo:
protected function setUp(): void {
parent::setUp();
$this->kernel = $this->getMock('Symfony\Component\HttpKernel\HttpKernelInterface');
$this->banManager = $this->getMock('Drupal\ban\BanIpManagerInterface');
$this->banMiddleware = new BanMiddleware($this->kernel, $this->banManager);
}
Luego indicamos que para el método 'doSomething' (method()), devuelva el valor 'foo' (willReturn()). Esto implica que, al hacer una llamada a $this->bar->doSomething(), el valor devuelto será 'foo'.
Esta es la forma de testear clases que dependen de objetos de otras clases, evitando dependencias que puedan romper la ejecución del test o dar resultados inesperados.
TODO: Ampliar más sobre esto si hace falta
Test Funcionales (PHPUnit)
Reference: https://deninet.com/blog/2019/01/13/writing-automated-tests-drupal-8-part-2-functional-tests
Primero creamos el sistema de directorios:
modules/custom/my_module
├── mymodule.info.yml
├── mymodule.module
├── src/
└── tests/
└── src/
└── Functional/
Para crear un test lo metemos en la siguiente carpeta:
tests/src/Functional/NombreDelTestTest.php
El contenido sería:
<?php
namespace Drupal\Tests\mymodule\Functional;
use Drupal\Tests\BrowserTestBase;
/**
* Test the module settings page
*
* @group mymodule
*/
class SettingsPageTest extends BrowserTestBase {
/**
* The modules to load to run the test.
*
* @var array
*/
public static $modules = [
'user',
'mymodule',
];
/**
* {@inheritdoc}
*/
protected function setUp(): void {
parent::setUp();
}
}
Consideraciones:
-
Usamos en los Annotations la directiva @group para agrupar tests similares
-
Para que la clase pueda hacer tests debe ser extendida:
\Drupal\Tests\BrowserTestBase
-
Al usar
BrowserTestBase
debemos implementar, o al menos definir$modules
ysetUp()
- $modules se usa para activar módulos necesarios a la hora de realizar un test
-
Por conveniencia la clase test usa el sufijo Test
- Ejemplo:
/mymodule/tests/src/Funtional/FooAddTest.php
- Ejemplo:
-
Los métodos de cada test no tienen argumentos y empiezan por la palabra test:
- Ejemplo:
testFooAddBar()
- Ejemplo:
Métodos assert*()
- Usaremos métodos assert del tipo $this->assertSession()-> métodoAssert ();
- Métodos disponibles: https://api.drupal.org/api/drupal/core%21tests%21Drupal%21Tests%21WebAssert.php/class/WebAssert/8.2.x
Ejemplos:
# Ejemplo 1
$assert = $this->assertSession()->pageTextContains('foo text');
# Ejemplo 2
$assert = $this->assertSession();
$assert->pageTextContains('foo text');
Ejemplo completo:
/modules/contrib/examples/cron_example/tests/src/Functional/CronExampleTest.php
https://git.drupalcode.org/project/examples/-/blob/3.x/modules/cron_example/tests/src/Functional/CronExampleTest.php
Explicación del código:
- $modules contiene los módulos a ser instalados para realizar el test
public static $modules = ['cron_example', 'node'];
- Dentro del setUp() se crea un nuevo usuario con sus permisos
public function setUp(): void {
parent::setUp();
// Create user. Search content permission granted for the search block to
// be shown.
$this->drupalLogin($this->drupalCreateUser(['administer site configuration', 'access content']));
}
- Se utiliza el método assertSession() para acceder a comprobaciones assert adicionales.
$assert = $this->assertSession();
- Cargamos la página dado el route
$cron_form = Url::fromRoute('cron_example.description');
- Obtener la página
$this->drupalGet($cron_form);
- Se envía el formulario
$this->drupalPostForm(NULL, $post, 'Run cron now');
- Miramos si hay el siguiente string
$assert->pageTextContains('cron_example executed at');
- Comprobación si hay texto con expresión regular
$assert->responseMatches('/Queue 2 worker processed item with sequence 100 /');
Mas info (tutorial) en https://www.drupal.org/node/2783189
Test Funcionales (Behat)
...