viernes, 28 de septiembre de 2018

Plantilla para proyectos en PHP con TDD y DDD con composer


En esta publicación voy a explicar cómo implementar la base para un proyecto o librería que queramos desarrollar con TDD + DDD utilizando Composer desde cero.

Para desarrollar con TDD vamos a necesitar un framework de pruebas. En este caso se va a utilizar PHPUnit que es un framework de pruebas muy extendido. Para instalar los paquetes como PHPUnit disponemos de varias herramientas siendo Composer de las más comunes.

Otro problema al que nos enfrentamos a la hora de llevar a cabo el proyecto es la configuración del fichero de composer para que todo funcione correctamente. Así que voy a explicar cómo llevar a cabo cada paso para tener un proyecto desarrollado con TDD + DDD con Composer.

Por supuesto, el control de versiones es imprescindible en cualquier proyecto así que en este caso se va a utilizar Git.

Lo primero es crear una carpeta con el nombre del proyecto. Así que, suponiendo que tenemos una carperta “projects” en nuestra home de usuario, desde una consola creamos la carpeta con el nombre del proyecto, por ejemplo: “php-tdd-ddd-template-composer”.

mkdir ~/projects/php-tdd-ddd-template-composer

Una vez creada accedemos a ella.

cd ~/projects/php-tdd-ddd-template-composer

Ahora hay que iniciar un proyecto en Git. Así que creamos nuestro flamante proyecto en git con el siguiente comando.

git init

Siempre es recomendable añadir un archivo README.md ya que tanto para otros usuarios como para nosotros mismo en un futuro nos va a ser útil. No hay un estándar para hacer ficheros de este estilo aunque seguro que con una rápida búsqueda puedes encontrar recomendaciones o plantillas.

Una vez creado el archivo README.md lo incluimos en el repositorio.

git add .

Hacemos “commit” del mismo.

git commit -m “Add README.md” file.

Creado el proyecto y el repositorio, el siguiente paso es crear un archivo de composer para que sea más fácil la administración de los distintos paquetes.

composer init

Este comando va a crear una serie de preguntas

Package name (<vendor>/<name>) [eltortuganegra/php-tdd-template-composer]:
Description []: Template for php projects for develop with TDD. Composer is used for install project.
Author [, n to skip]: Jorge Sánchez <jorge.sanchez@eltortuganegra.com>
Minimum Stability []:
Package Type (e.g. library, project, metapackage, composer-plugin) []:
License []: MIT
Would you like to define your dependencies (require) interactively [yes]?
Search for a package:
Would you like to define your dev dependencies (require-dev) interactively [yes]? Yes
Search for a package: phpunit

Found 15 packages matching phpunit

[0] phpunit/phpunit
[1] phpunit/phpunit-mock-objects
[2] phpunit/php-token-stream
[3] phpunit/php-timer
[4] phpunit/php-text-template
[5] phpunit/php-file-iterator
[6] phpunit/php-code-coverage
[7] symfony/phpunit-bridge
[8] phpunit/phpunit-selenium
[9] johnkary/phpunit-speedtrap
[10] codedungeon/phpunit-result-printer
[11] jean85/pretty-package-versions
[12] brianium/paratest
[13] phpunit/dbunit
[14] spatie/phpunit-snapshot-assertions

Enter package # to add, or the complete package name if it is not listed: 0

Los paquetes que vamos a instalar son, de momento, phpunit en su última versión.
En este caso añadimos un 0 y pulsamos enter, lo que nos lleva a buscar otro paquete. Como no queremos añadir más paquetes. Pulsamos enter.

Para ver las dependencias escribimos “yes” y pulsamos enter.

Search for a package:

{
"name": "eltortuganegra/php-tdd-template-composer",
"description": "Template for php projects for develop with TDD. Composer is used for install project.",
"type": "project",
"require-dev": {
"phpunit/phpunit": "^7.3"
},
"license": "MIT",
"authors": [
{
"name": "eltortuganegra",
"email": "jorge.sanchez@eltortuganegra.com"
}
]
}

Do you confirm generation [yes]? yes
Would you like the vendor directory added to your .gitignore [yes]? yes


Sólo queda confirmar y se procede a la generación del archivo “composer.json”.

Ahora pasamos a instalar con composer todas las dependencias.

composer install

En este momento se puede observar como se ha creado una carpeta «vendor» donde se han instalado todas las dependencias.

Llega el momento de organizar las distintas carpetas del proyecto. Cada uno tiene su organización de carpetas pero una buena manera de empezar es crear una carpeta «tests» y otra «src» donde se almacenará el código de los tests (sí, ¡las pruebas se hacen primero!).

Para comprobar si Phpunit está funcionando vamos a crear una prueba por defecto: DefaultTest.php

Cuando ejecutamos phpunit para que ejecute la prueba

./vendor/bin/phpunit tests/DefaultTest.php

Debería devolver el siguiente resultado.

PHPUnit 7.3.5 by Sebastian Bergmann and contributors.

. 1 / 1 (100%)

Time: 98 ms, Memory: 4.00MB

OK (1 test, 1 assertion)

Con esta sencilla prueba se verifica que Phpunit está funcionando correctamente. Aún que el propósito es probar el código fuente así que vamos a añadir una prueba donde se utilice una clase que esté ubicada en la carpeta destinada a tal fin, la carpeta «src».

Una prueba simple podría ser crear una instancia de la clase ubicada en la carpeta «src» y comprobar que la nueva instancia es una instancia de esa clase.

Ejecutamos la prueba recién creada para confirmar que da un error, tal y como se indica en TDD.

PHPUnit 7.3.5 by Sebastian Bergmann and contributors.

.E 2 / 2 (100%)

Time: 83 ms, Memory: 4.00MB

There was 1 error:

1) DefaultTest::testDeleteMeShouldCreateAnInstanceWhenWeCreateAnInstance
Error: Class 'DeleteMe' not found

/home/vagrant/www/php-tdd-template-composer/tests/DefaultTest.php:17

ERRORS!
Tests: 2, Assertions: 1, Errors: 1.

Como se puede apreciar la ejecución de la prueba da un error. Ahora hay que crear la clase “DeleteMe” en la carpeta «src» para que pasar la prueba. Añadimos el namespace «app».
En este caso, si volvemos a ejecutar la prueba vamos a tener el mismo error. ¿Cómo es posible si hemos creado la clase? El problema es que no estamos indicando que cargue las clases automáticamente. Para configurarlo hay que modificar el fichero de configuración de composer y añadirle un atributo “autoload”.

{
"name": "eltortuganegra/php-tdd-template-composer",
"description": "Template for php projects for develop with TDD. Composer is used for install project.",
"type": "project",
"require-dev": {
"phpunit/phpunit": "^7.3"
},
"license": "MIT",
"authors": [
{
"name": "eltortuganegra",
"email": "jorge.sanchez@eltortuganegra.com"
}
],
"autoload": {
"classmap": [
"src/"
]
}
}

Actualizamos composer para que incorpore los cambios en el fichero de configuración.

composer dump-autoload

Y volvemos a ejecutar la prueba. Con el siguiente resultado.

./vendor/bin/phpunit tests/DefaultTest.php
PHPUnit 7.3.5 by Sebastian Bergmann and contributors.

.. 2 / 2 (100%)

Time: 67 ms, Memory: 4.00MB

OK (2 tests, 2 assertions)

Llegados a este punto, tenemos la base del proyecto funcionando. Una carpeta «tests» donde se almacenan todos las pruebas y una carpeta «src» donde se almacena todo el código que tiene que pasar las pruebas.

Según el DDD no hay una organización estándar de estructuras así que cada uno es libre de organizarla como considere oportuno. En el libro de Eric Evans Domain-Driven Design book tiene un ejemplo “Cargo Sample” del cual se puede encontrar implementaciones en GitHub como como el php-ddd-cargo-sample. En el se puede apreciar la estructura de carpetas que utilizan.

Sin embargo, esta publicación es para crear un proyecto sencillo y de cero por lo que, en principio y como ejercicio paso a paso, crearía una carpeta por cada concepto que se detalla en DDD.

Mi solución sencilla para aquellos que están empezando y quieren empezar a introducirse en este mundo es utilizar la plantilla para proyectos en php con TDD y DDD.

En esta plantilla hay una estructura de carpetas que albergan un clase por defecto por cada concepto y su correspondiente prueba en la carpeta «tests».


1 comentario:

  1. Buenas,
    me han contado de una herramienta de "scaffolding" que podría ser interesante (se podría poner el nombre y email del developer como variables)

    https://yeoman.io/

    ResponderEliminar