() translation by (you can also view the original English article)
Como desarrollador de PHP, puede utilizar la técnica de desarrollo controlado por pruebas (TDD) para desarrollar su software mediante pruebas de escritura. Típicamente, TDD dividirá cada tarea del desarrollo en unidades individuales. A continuación, se escribe una prueba para asegurarse de que la unidad se comporta como se esperaba.
Cada proyecto que utiliza el desarrollo impulsado por pruebas sigue tres pasos sencillos repetidamente:
- Escriba una prueba para el siguiente bit de funcionalidad que desea agregar.
- Escriba el código funcional hasta que pase la prueba.
- Refactorizar el código nuevo y viejo para hacerlo bien estructurado.
Continúe haciendo un ciclo a través de estos tres pasos, una prueba a la vez, aumentando la funcionalidad del sistema. Las pruebas le ayudarán a refactorizar, lo que le permite mejorar su diseño con el tiempo y hace que algunos problemas de diseño sean más obvios.
Las pruebas que contienen pequeños componentes individuales se denominan pruebas unitarias. Si bien las pruebas de unidad pueden realizarse independientemente, si prueba algunos de los componentes cuando están integrados con otros componentes, está realizando pruebas de integración. El tercer tipo de prueba es trozos de prueba. Los stubs de prueba le permiten probar su código sin tener que realizar llamadas reales a una base de datos.
Por qué TDD funciona
Hoy en día, como usted puede utilizar la sintaxis moderna del IDE de PHP, la regeneración no es una gran cosa. Uno de los aspectos importantes de su desarrollo es asegurarse de que el código hace lo que usted espera que haga. Como el software es complicado (diferentes componentes integrados entre sí), sería difícil que todas nuestras expectativas se hicieran realidad. Especialmente al final del proyecto, debido a su desarrollo, el proyecto se volverá más complejo, y por lo tanto más difícil de depurar y probar.
TDD verifica que el código hace lo que usted espera que haga. Si algo sale mal, sólo hay unas pocas líneas de código para volver a comprobar. Los errores son fáciles de encontrar y corregir. En TDD, la prueba se centra en el comportamiento, no en la implementación. TDD proporciona código probado que ha sido probado, diseñado y codificado.
PHPUnit & Laravel
PHPUnit es el estándar de facto para probar unidades PHP. Es esencialmente un framework para escribir pruebas y proporcionar las herramientas que necesitará para ejecutar pruebas y analizar los resultados. PHPUnit deriva su estructura y funcionalidad de SUnit de Kent Beck.
Hay varias afirmaciones diferentes que pueden ayudarle a probar los resultados de todo tipo de llamadas en sus aplicaciones. A veces tienes que ser un poco más creativo para probar una pieza más compleja de funcionalidad, pero las aseveraciones proporcionadas por PHPUnit cubren la mayoría de los casos que querrías probar. Aquí está una lista de algunas de las más comunes que se encontrará usando en sus pruebas:
- AssertTrue: Comprueba la entrada para verificar que es igual a verdadero.
- AssertFalse: Comprueba la entrada para verificar que es igual a valor falso.
- AssertEquals: Comprueba el resultado en contra de otra entrada para una coincidencia.
- AssertArrayHasKey(): informa de un error si la array no tiene la clave.
- AssertGreaterThan: Comprueba el resultado para ver si es mayor que un valor.
- AssertContains: Comprueba que la entrada contiene cierto valor.
- AssertType: Comprueba que una variable es de cierto tipo.
- AssertNull: Comprueba que una variable es null.
- AssertFileExists: Comprueba que existe un archivo.
- AssertRegExp: Comprueba la entrada con una expresión regular.
De forma predeterminada, PHPUnit 4.0 se instala en Laravel y puede ejecutar el siguiente comando para actualizarlo:
1 |
composer global require "phpunit/phpunit=5.0.*"
|
El archivo phpunit.xml
del directorio raíz de Laravel le permitirá hacer algunas configuraciones. En este caso, si desea anular la configuración predeterminada, puede editar el archivo:
1 |
<?xml version="1.0" encoding="UTF-8"?>
|
2 |
<phpunit backupGlobals="false" |
3 |
backupStaticAttributes="false" |
4 |
bootstrap="bootstrap/autoload.php" |
5 |
colors="true" |
6 |
convertErrorsToExceptions="true" |
7 |
convertNoticesToExceptions="true" |
8 |
convertWarningsToExceptions="true" |
9 |
processIsolation="false" |
10 |
stopOnFailure="false" |
11 |
syntaxCheck="false"> |
12 |
<testsuites>
|
13 |
<testsuite name="Application Test Suite"> |
14 |
<directory>./tests/</directory> |
15 |
</testsuite>
|
16 |
</testsuites>
|
17 |
<filter>
|
18 |
<whitelist>
|
19 |
<directory suffix=".php">app/</directory> |
20 |
</whitelist>
|
21 |
</filter>
|
22 |
<php>
|
23 |
<env name="APP_ENV" value="testing"/> |
24 |
<env name="CACHE_DRIVER" value="array"/> |
25 |
<env name="SESSION_DRIVER" value="array"/> |
26 |
<env name="QUEUE_DRIVER" value="sync"/> |
27 |
<env name="DB_DEFAULT" value="sqlite_testing"/> |
28 |
<env name="DB_HOST" value="localhost"/> |
29 |
<env name="DB_DATABASE" value="laravel"/> |
30 |
<env name="DB_USERNAME" value="root"/> |
31 |
<env name="DB_PASSWORD" value="root"/> |
32 |
</php>
|
33 |
</phpunit>
|
34 |
Como se ve en el código anterior, he añadido la configuración de la base de datos de muestra (no utilizada en el artículo).
¿Qué es Doctrine ORM?
Doctrine es un ORM que implementa el patrón de asignación de datos y le permite hacer una separación clara de las reglas de negocio de la aplicación de la capa de persistencia de la base de datos. Para configurar Doctrine, hay un puente para permitir que coincida con la configuración existente de Laravel 5. Para instalar Doctrine 2 dentro de nuestro proyecto Laravel, ejecutamos el siguiente comando:
1 |
composer require laravel-doctrine/orm |
Como de costumbre, el paquete se debe agregar a la app/config.php
, como el proveedor de servicios:
1 |
LaravelDoctrine\ORM\DoctrineServiceProvider::class, |
El alias también debe configurarse:
1 |
'EntityManager' => LaravelDoctrine\ORM\Facades\EntityManager::class |
Finalmente, publicamos la configuración del paquete con:
1 |
php artisan vendor:publish --tag="config" |
Cómo probar los repositorios de Doctrine
Antes de cualquier otra cosa, usted debe saber sobre los fixtures. Fixtures se utilizan para cargar un conjunto controlado de datos en una base de datos, que necesitamos para las pruebas. Afortunadamente, Doctrine 2 tiene una biblioteca para ayudarle a escribir fixtures para el Doctrine ORM.
Para instalar el paquete de accesorios en nuestra aplicación Laravel, necesitamos ejecutar el siguiente comando:
1 |
composer require --dev doctrine/doctrine-fixtures-bundle
|
Vamos a crear nuestro dispositivo en tests/Fixtures.php
:
1 |
namespace Test; |
2 |
use Doctrine\Common\Persistence\ObjectManager; |
3 |
use Doctrine\Common\DataFixtures\FixtureInterface; |
4 |
use app\Entity\Post; |
5 |
|
6 |
class Fixtures implements FixtureInterface |
7 |
{
|
8 |
/**
|
9 |
* Load the Post fixtures
|
10 |
* @param ObjectManager $manager
|
11 |
* @return void
|
12 |
*/
|
13 |
public function load(ObjectManager $manager) |
14 |
{
|
15 |
$Post = new Post(['title'=>'hello world','body'=>'this is body']); |
16 |
$manager->persist($Post); |
17 |
$manager->flush(); |
18 |
}
|
19 |
}
|
20 |
Como
puedes ver, tu clase de dispositivo debe implementar FixtureInterface
y
debe tener el método load (ObjectManager $manager)
. Los fixtures de Doctrine2 son clases de PHP donde puedes crear objetos y persistirlos en la base de datos. Para autoload nuestras instalaciones en Laravel, necesitamos modificar composer.json
en nuestra raíz de Laravel:
1 |
... |
2 |
"autoload-dev": { |
3 |
"classmap": [ |
4 |
"tests/TestCase.php", |
5 |
"tests/Fixtures.php" //added here |
6 |
] |
7 |
}, |
8 |
... |
Entonces ejecuta:
1 |
composer dump-autoload |
Vamos a crear nuestro archivo de prueba en el directorio de pruebas DoctrineTest.php
.
1 |
namespace Test; |
2 |
use App; |
3 |
use App\Entity\Post; |
4 |
use Doctrine\Common\DataFixtures\Executor\ORMExecutor; |
5 |
use Doctrine\Common\DataFixtures\Purger\ORMPurger; |
6 |
use Doctrine\Common\DataFixtures\Loader; |
7 |
use App\Repository\PostRepo; |
8 |
|
9 |
class doctrineTest extends TestCase |
10 |
{
|
11 |
private $em; |
12 |
private $repository; |
13 |
private $loader; |
14 |
public function setUp() |
15 |
{
|
16 |
parent::setUp(); |
17 |
$this->em = App::make('Doctrine\ORM\EntityManagerInterface'); |
18 |
$this->repository = new PostRepo($this->em); |
19 |
$this->executor = new ORMExecutor($this->em, new ORMPurger); |
20 |
$this->loader = new Loader; |
21 |
$this->loader->addFixture(new Fixtures); |
22 |
}
|
23 |
|
24 |
/** @test */
|
25 |
public function post() |
26 |
{
|
27 |
$purger = new ORMPurger(); |
28 |
$executor = new ORMExecutor($this->em, $purger); |
29 |
$executor->execute($this->loader->getFixtures()); |
30 |
$user = $this->repository->PostOfTitle('hello world'); |
31 |
$this->em->clear(); |
32 |
$this->assertInstanceOf('App\Entity\Post', $user); |
33 |
}
|
34 |
}
|
En el método setUp()
, instanciar el ORMExecutor
y el cargador. También cargamos la clase Fixtures
que acabamos de implementar.
No
olvide que la anotación /** @test */
es muy importante, y sin esto el
phpunit devolverá un error No tests found in class
.
Para comenzar a probar en la raíz de nuestro proyecto, ejecute el comando:
1 |
sudo phpunit
|
El resultado sería:
1 |
PHPUnit 4.6.6 by Sebastian Bergmann and contributors. |
2 |
|
3 |
Configuration read from /var/www/html/laravel/phpunit.xml.
|
4 |
Time: 17.06 seconds, Memory: 16.00M |
5 |
OK (1 test, 1 assertion) |
Si desea compartir objetos entre dispositivos, es posible agregar fácilmente una referencia a ese objeto por nombre y posteriormente referenciarlo para formar una relación. Aquí hay un ejemplo:
1 |
namespace Test; |
2 |
use Doctrine\Common\Persistence\ObjectManager; |
3 |
use Doctrine\Common\DataFixtures\FixtureInterface; |
4 |
use app\Entity\Post; |
5 |
|
6 |
class PostFixtures implements FixtureInterface |
7 |
{
|
8 |
/**
|
9 |
* Load the User fixtures
|
10 |
*
|
11 |
* @param ObjectManager $manager
|
12 |
* @return void
|
13 |
*/
|
14 |
public function load(ObjectManager $manager) |
15 |
{
|
16 |
$postOne = new Post(['title'=>'hello','body'=>'this is body']); |
17 |
$postTwo = new Post(['title'=>'hello there ','body'=>'this is body two']); |
18 |
$manager->persist($postOne); |
19 |
$manager->persist($postTwo); |
20 |
$manager->flush(); |
21 |
|
22 |
// store reference to admin role for User relation to Role
|
23 |
$this->addReference('new-post',$postOne); |
24 |
}
|
25 |
}
|
Y el fixture Comment:
1 |
namespace Test; |
2 |
use Doctrine\Common\Persistence\ObjectManager; |
3 |
use Doctrine\Common\DataFixtures\FixtureInterface; |
4 |
use app\Entity\Post; |
5 |
|
6 |
class CommentFixtures implements FixtureInterface |
7 |
{
|
8 |
/**
|
9 |
* Load the User fixtures
|
10 |
*
|
11 |
* @param ObjectManager $manager
|
12 |
* @return void
|
13 |
*/
|
14 |
public function load(ObjectManager $manager) |
15 |
{
|
16 |
$comment = new Comment(['title'=>'hello','email'=>'alirezarahmani@live.com','text'=>'nice post']); |
17 |
$comment->setPost($this->getReference('new-post')); // load the stored reference |
18 |
$manager->persist($comment); |
19 |
$manager->flush(); |
20 |
// store reference to new post for Comment relation to post
|
21 |
$this->addReference('new-post',$postOne); |
22 |
}
|
23 |
}
|
Con dos métodos de getReference()
y setReference()
, puede compartir objetos entre dispositivos.
Si
el orden de fixtures es importante para usted, puede ordenarlos
fácilmente con el método getOrder
en sus fixtures de la siguiente
manera:
1 |
public function getOrder() |
2 |
{
|
3 |
return 5; // number in which order to load fixtures |
4 |
}
|
Observe que el orden es relevante para la clase Loader.
Una de las cosas importantes acerca de los accesorios es su capacidad para resolver problemas de dependencia. La única cosa que usted necesita agregar es un método en su accesorio como hice abajo:
1 |
public function getDependencies() |
2 |
{
|
3 |
return array('Test\CommentFixtures'); // fixture classes fixture is dependent on |
4 |
}
|
Conclusion
Esta es sólo una descripción de Desarrollo de prueba con Laravel 5 y PHPUnit. Al probar los repositorios, es inevitable que va a usar la base de datos. En este caso, los fixtures de Doctrine son importantes.