Test de proyectos existentes
En este capítulo veremos como están desarrollados los tests de algunos proyectos existentes. Veremos que frameworks de tests están utilizando, cobertura de test, tipo de integración continua si está configurado en algún proyecto público como Travis CI [35].
Para hacer un análisis más profundo de la calidad del código de testeo de los proyectos que escogeremos, utilizaremos una herramienta específica para mutación de tests: Humbug.
12.1 Humbug
Humbug [36] es un framework para mutación de tests, con el objetivo de medir la eficacia real del conjunto de tests con el objetivo de mejorarlo.
Humbug realiza un análisis del código y, de forma dinámica, realiza pequeñas mutaciones con el objetivo de que los tests no pasen. Esto indicará que el tests está bien pensado he implementado. Dado que los tests están pensados para prevenir una regresión, imponer al sistema una regresión real que no sea detectada por nuestros tests implica que algo no está tan bien como parecía.
Hay que tener en cuenta que Humbug está en modo experimental, por lo que el resultado del análisis que haremos a continuación debe ser considerado como un experimento.
Para usar Humbug debemos instalarlo en el proyecto que deseemos testear. Esto puede realizarse mediante la instalación de un fichero .phar en el sistema, o bien mediante Composer [11] de forma global en el sistema:
composer global require 'humbug/humbug=~1.0@dev'
Para ejecutar Humbug necesitamos un fichero de configuración, llamado "humbug.json.dist" o "humbug.json", en la raíz de nuestro proyecto con la configuración necesaria. Una configuración básica y auto explicativa sería:
{
"timeout": 10,
"source": {
"directories": [
"src"
]
},
"logs": {
"text": "humbuglog.txt",
"json": "humbuglog.json"
}
}
Para ejecutar Humbug solo debemos lanzar el comando "humbug".
Los resultados de Humbug se deben interpretar de la siguiente forma:
Mutación asesinada (.): Una mutación ha causado que el test falle, lo cual es una salida positiva.
Mutación escapada (E): Una mutación no ha conseguido que el test falle, por lo cual la consideramos como un error, dado que esperamos que un test falle para un cambio de código.
Mutación no cubierta (S): Una mutación que ocurre en una linea no estaba cubierta por ningún test unitario. Dado que no tiene test, consideramos esta salida como errónea.
Fatal Error (F): Una mutación ha generado un fatal error. Estas situaciones pueden deberse a errores del propio Humbug, o a una salida positiva en nuestro entendimiento, dado que la mutación a provocado una inestabilidad en la aplicación.
Timeout (T): Esto se debe a que el test unitario se excede en tiempo de ejecución configurado por Humbug. Lo consideramos como una salida positiva dado que un timeout debe ser un comportamiento inapropiado, a veces provocado porque una mutación acaba creando un bucle infinito.
Las métricas para Humbug deben ser consideradas:
Puntuación de indicación de mutaciones (Mutation Score Indicator) es 47%, esto significa que el 47 de todas las mutaciones generadas fueron detectadas (por ejemplo, asesinadas, timeouts o errores fatales). Si la covertura de tests fuera 65%, esto indica que tenemos un 18% de tests inestables.
Covertura de código por mutaciones (Mutation Code Coverage) es de 67%. En teoría debería de ser igual a la covertura de código generada por los tests, pero la covertura de tests ignora las mutaciones.
Mutaciones:
En la página oficial de Humbug se puede ver todos los tipos de mutaciones que puede realizar. Para ilustrar y ayudar a entender los cambios que realiza en el código añadimos la tabla 12.1 de mutaciones de lógica binaria:
Original | Mutado | ||
---|---|---|---|
true | false | ||
false | true | ||
&& | \ | \ | |
\ | \ | && | |
and | or | ||
or | and | ||
! |
: Mutaciones binarias
12.2 Silex
Silex [37] es un micro-framework desarrollado en PHP utilizando componentes de Symfony2 y Pimple como inyector de depencencias e inspirado en sinatra (framework desarrollado en Ruby).
Añadimos Silex en este apartado por entender que es una versión simplificada de los desarrollos que está realizando el equipo de Symfony2.
12.2.1 Tests en Silex
Para comenzar a explorar en los tests de Silex lo primero que necesitamos es descargar el código e instalar las dependencias utilizando Composer:
git clone https://github.com/silexphp/Silex.git
cd Silex
composer install
La versión sobre la que vamos a realizar el análisis es la 1.3.
Viendo la configuración de phpunit.xml.dist, tenemos una suite de tests alojada en el directorio tests/Silex.
Si ejecutamos los tests unitarios con la opción para ver la cobertura de tests obtendremos el resultado:
Cobertura de tests: 86.19% (1279 / 1484) Funciones y métodos: 81.92% (145 / 177) Clases y Traits: 52.17% (24 / 46)
La cobertura de código en lineas es bastante alta. Se considera de forma general que un proyecto con una cobertura de tests por encima del 70% está en una situación favorable.
Para ver si estos tests son lo eficientes que deberían ser, ejecutemos Humbug y veamos el resultado que obtenemos:
Humbug version 1.0-dev
Humbug running test suite to generate logs and code coverage data...
255 [==========================================================] 19 secs
Humbug has completed the initial test run successfully.
Tests: 255 Line Coverage: 86.16%
Humbug is analysing source files...
Mutation Testing is commencing on 48 files...
(.: killed, M: escaped, S: uncovered, E: fatal error, T: timed out)
M.M...MM....E...MMMM..MMMM..M..S.E...SM..M.......MM.E....M.M | 60 (12/48)
.S......MMMM..........EEE.SSE......E.M.........M............ | 120 (15/48)
....EM....MM...EE.......E.MEE...MMMMMMMS.................... | 180 (16/48)
.....M..ME.MMME...ESS..M..S..S..SS...SSSSSSSSSSSSSS.EEE..... | 240 (32/48)
..E.................M.........M....ESSMMM..............S..M. | 300 (38/48)
..E.E.M...M...SSS..................EM.MMM.M...E
347 mutations were generated:
234 mutants were killed
32 mutants were not covered by tests
55 covered mutants were not detected
26 fatal errors were encountered
0 time outs were encountered
Metrics:
Mutation Score Indicator (MSI): 75%
Mutation Code Coverage: 91%
Covered Code MSI: 83%
Remember that some mutants will inevitably be harmless (i.e. false positives).
Time: 2.39 minutes Memory: 17.00MB
Humbug results are being logged as TEXT to: humbuglog.txt
Viendo el resultado de Humbug, tenemos un 75% de MSI, mientras que la cobertura era del 86%, por lo cual tenemos un 11% de los tests que no han respondido correctamente a una mutación. Teniendo en cuenta que según la propia documentación de Humbug, es posible encontrar falsos positivos, parece un resultado bastante coherente.
Silex utiliza las herramientas ofrecidas por PHPUnit para mockear, aunque hay relativamente pocos mocks. De las 59 clases de tests, 20 utilizan algún tipo de mock.
12.2.2 Test de integración de Silex
Silex está integrado utilizando la herramienta online Travis CI. Esto podemos saberlo mirando el fichero de configuración .travis.yml, situado en la raiz del proyecto.
El contenido de dicho fichero es el siguiente:
language: php
sudo: false
cache:
directories:
- $HOME/.composer/cache
before_script:
# symfony/*
- sh -c "if [ '$TWIG_VERSION' != '2.0' ]; then sed -i 's/~1.8|~2.0/~1.8/g' composer.json;
composer update; fi"
- sh -c "if [ '$SYMFONY_DEPS_VERSION' = '3.0' ];
then sed -i 's/~2\.3|3\.0\.\*/3.0.*@dev/g' composer.json; composer update; fi"
- sh -c "if [ '$SYMFONY_DEPS_VERSION' = '2.8' ];
then sed -i 's/~2\.3|3\.0\.\*/2.8.*@dev/g' composer.json; composer update; fi"
- sh -c "if [ '$SYMFONY_DEPS_VERSION' = '' ];
then sed -i 's/~2\.3|3\.0\.\*/2.7.*@dev/g' composer.json; composer update; fi"
- sh -c "if [ '$SYMFONY_DEPS_VERSION' = '2.3' ];
then sed -i 's/~2\.3|3\.0\.\*/2.3.*@dev/g' composer.json; composer update; fi"
- composer install
script: phpunit
matrix:
include:
- php: 5.3
- php: 5.4
- php: 5.5
- php: 5.6
env: TWIG_VERSION=2.0
- php: 5.6
env: SYMFONY_DEPS_VERSION=2.3
- php: 5.6
env: SYMFONY_DEPS_VERSION=2.8
- php: 5.6
env: SYMFONY_DEPS_VERSION=3
- php: 7.0
- php: hhvm
Viendo el fichero de configuración podemos apreciar que Silex funcionará para las versiones de PHP desde la 5.3 hasta la 7.0. Además, además, el script de instalación realiza modificaciones sobre el fichero de composer.json" para adaptarlo a las versiones de los distintos módulos de Symfony2* instalados.
12.3 Slim
Slim [38] es un micro-framework escrito en PHP, que según el propio proyecto, ayuda a desarrollar rápidamente aplicaciones web y API.
Desarrollado por Josh Lockhart, autor del libro Modern PHP [5] de la popular página web "PHP The Right Way".
La simplicidad the Slim como framework se puede ver con el clásico "hello world" que aparece como ejemplo al comienzo de la documentación:
$app = new \Slim\Slim();
$app->get('/hello/:name', function ($name) {
echo "Hello, $name";
});
$app->run();
12.3.1 Test en Slim
Para comenzar a explorar los tests de Slim necesitamos descargarnos el código fuente e instalar las dependencias:
git clone https://github.com/slimphp/Slim.git
cd Slim
composer install
La versión de Slim sobre la que estamos estudiando los tests es la 2.6.2, aunque ya hay una versión beta de Slim 3.
Tras la ejecución de los tests unitarios, la cobertura de Slim sería la siguiente:
Cobertura de tests: 94.31% (1276 / 1353)
Funciones y métodos: 88.29% (264 / 299)
Clases y Traits: 71.43% (15 / 21)
La cobertura de tests de *Slim" es del 94% del código.
Como hicimos en el caso de Silex, para evaluar la calidad de los tests unitarios de Slim ejecutaremos Humbug. El resultado obtenido por la ejecución es:
Humbug version 1.0-dev
Humbug running test suite to generate logs and code coverage data...
395 [==========================================================] 11 secs
Humbug has completed the initial test run successfully.
Tests: 395 Line Coverage: 94.31%
Humbug is analysing source files...
Mutation Testing is commencing on 21 files...
(.: killed, M: escaped, S: uncovered, E: fatal error, T: timed out)
.............MMM.......E............E..M.................M.. | 60 ( 3/21)
.M..MM.............MSM...MMMM...........MM....M.M.MM....M.M. | 120 ( 5/21)
M.M........MMM.M..........M.MS..M.M..M...M.M..M.........M..M | 180 ( 6/21)
........M...........MM....................MMM....MMM........ | 240 ( 7/21)
.......M.............M.......M..M....................M...MMM | 300 (13/21)
..M.M.MSSSSS...M...MM.TEEEEEEEE.EE...M...MMS........M....... | 360 (16/21)
...E........SSS.M..M...M.M...M.M..SSS.....MME....E.........M | 420 (19/21)
MMSM.......
431 mutations were generated:
323 mutants were killed
15 mutants were not covered by tests
77 covered mutants were not detected
15 fatal errors were encountered
1 time outs were encountered
Metrics:
Mutation Score Indicator (MSI): 79%
Mutation Code Coverage: 97%
Covered Code MSI: 81%
Remember that some mutants will inevitably be harmless (i.e. false positives).
Time: 3.13 minutes Memory: 18.75MB
Humbug results are being logged as TEXT to: humbuglog.txt
El valor de MSI de Slim es de 79% frente al 94% de la cobertura de tests unitarios, esto es un 15% de diferencia. Esto nos indica que hay un conjunto de tests mayor que en Silex (proporcionalmente), que no están testeando de la mejor forma el código, debido a que el test ha pasado después de que se han realizado mutaciones en el código.
12.3.3 Test de integración de Slim
Al igual que Silex, Slim está integrado utilizando Travis CI. El contenido del fichero de configuración .travis.yml para este proyecto es:
language: php
php:
- 5.3
- 5.4
- 5.5
- 5.6
- 7.0
- hhvm
script: phpunit --coverage-text
Como podemos ver, la configuración es muy sencilla, teniendo en cuenta solo las versiones de PHP para las que Slim es compatible (desde la 5.3 hasta la 7.0, incluyendo hhvm) y la generación de la cobertura en formato texto.