Ce package est obsolète, consultez plutôt Infection.
Humbug est un framework de tests de mutation pour PHP. Il est actuellement en développement et donc, même s'il fonctionne plutôt bien, il aura des aspérités qu'une équipe de serviteurs travaille dur pour résoudre. S'il tombe hors du portail, vous êtes prévenu ;).
$ git remote set-url upstream https://github.com/humbug/humbug.git
Remplacez upstream
par le nom de la télécommande que vous utilisez localement ; upstream
est couramment utilisé, mais vous utilisez peut-être autre chose. Vous pouvez également utiliser une URL différente (par exemple [email protected]:mockery/mockery.git). Exécutez git remote -v
pour voir ce que vous utilisez réellement.
Table des matières
Les tests de mutation consistent, en un mot, à donner à vos tests unitaires une chance d'en avoir pour leur argent. Il s’agit d’injecter de petits défauts dans le code source puis de vérifier si les tests unitaires l’ont remarqué. Si c'est le cas, alors vos tests unitaires ont "tué" la mutation. Dans le cas contraire, la mutation a échappé à la détection. Comme les tests unitaires sont destinés à prévenir les régressions, avoir une véritable régression qui passe inaperçue serait une mauvaise chose !
Alors que la couverture du code peut vous indiquer quel code vos tests exécutent, les tests de mutation sont destinés à vous aider à évaluer les performances réelles de vos tests unitaires et les domaines dans lesquels ils pourraient être améliorés.
J'ai écrit plus en détail sur les raisons pour lesquelles les tests de mutation valent la peine : Mensonges, foutus mensonges et couverture du code : vers des tests de mutation
Humbug est un projet open source qui accueille les demandes d'extraction et les problèmes de n'importe qui. Avant d'ouvrir des demandes de tirage, veuillez lire notre court guide de contribution.
Vous pouvez cloner et installer les dépendances de Humbug à l'aide de Composer :
git clone https://github.com/humbug/humbug.git
cd humbug
/path/to/composer.phar install
La commande humbug est maintenant dans bin/humbug.
Si vous ne souhaitez pas suivre directement la branche principale, vous pouvez installer le phar Humbug comme suit :
wget https://padraic.github.io/humbug/downloads/humbug.phar
wget https://padraic.github.io/humbug/downloads/humbug.phar.pubkey
# If you wish to make humbug.phar directly executable
chmod +x humbug.phar
Sous Windows, vous pouvez simplement télécharger à l'aide d'un navigateur ou depuis Powershell v3 en utilisant les commandes suivantes où wget
est un alias pour Invoke-WebRequest
:
wget https://padraic.github.io/humbug/downloads/humbug.phar -OutFile humbug.phar
wget https://padraic.github.io/humbug/downloads/humbug.phar.pubkey -OutFile humbug.phar.pubkey
Si vous êtes bloqué avec Powershell v2 :
$client = new-object System.Net.WebClient
$client .DownloadFile( " https://padraic.github.io/humbug/downloads/humbug.phar " , " humbug.phar " )
$client .DownloadFile( " https://padraic.github.io/humbug/downloads/humbug.phar.pubkey " , " humbug.phar.pubkey " )
Le phar est signé avec une clé privée openssl. Vous aurez besoin que le fichier pubkey soit stocké à tout moment à côté du fichier phar pour pouvoir l'utiliser. Si vous renommez humbug.phar
en humbug
, par exemple, renommez également la clé humbug.phar.pubkey
en humbug.pubkey
.
Les versions de phar sont actuellement effectuées manuellement et ne seront donc pas mises à jour à la même fréquence que git master. Pour mettre à jour votre phar actuel, exécutez simplement :
./humbug.phar self-update
Remarque : L'utilisation d'un phar signifie que les correctifs peuvent prendre plus de temps pour atteindre votre version, mais vous avez plus d'assurance d'avoir une version de développement stable. La clé publique n'est téléchargée qu'une seule fois. Il est réutilisé par l'auto-mise à jour pour vérifier les futures versions de phar.
Une fois que les versions seront stables, il y aura une version alpha, bêta, RC et une version finale. Votre fichier phar de piste de développement se mettra automatiquement à jour jusqu'à ce qu'il atteigne une version stable. Si vous souhaitez continuer à suivre les phars du niveau de développement, vous devrez l'indiquer à l'aide de l'un des indicateurs de stabilité :
./humbug.phar self-update --dev
Si vous rencontrez des problèmes de mise à jour automatique avec des erreurs openssl
ou SSL inattendues, assurez-vous d'avoir activé l'extension openssl
. Sous Windows, vous pouvez le faire en ajoutant ou en décommentant la ligne suivante dans le fichier php.ini
pour PHP sur la ligne de commande (si différente du fichier de votre serveur http) :
extension=php_openssl.dll
Certaines autres erreurs SSL peuvent survenir en raison de certificats manquants. Vous pouvez remédier à ce problème en recherchant leur emplacement sur votre système (par exemple C:/xampp/php/ext/cacert.pem
), ou bien en téléchargeant une copie depuis http://curl.haxx.se/ca/cacert.pem. Assurez-vous ensuite que l'option suivante pointe correctement vers ce fichier :
openssl.cafile=C:/path/to/cacert.pem
En raison du fait que les dépendances de Humbug sont liées aux versions récentes, l'ajout de Humbug à composer.json peut donner lieu à des conflits. Les deux méthodes d'installation ci-dessus sont préférables lorsque cela se produit. Vous pouvez cependant l'installer globalement comme n'importe quel autre outil à usage général :
composer global require ' humbug/humbug=~1.0@dev '
Et si vous ne l'avez pas déjà fait... ajoutez ceci à ~/.bash_profile
(ou ~/.bashrc
) :
export PATH= ~ /.composer/vendor/bin: $PATH
Humbug fonctionne actuellement sur PHP 5.4 ou supérieur.
Humbug est encore en développement donc, je le répète, méfiez-vous des aspérités.
Pour configurer Humbug dans votre projet, vous pouvez exécuter :
humbug configure
Cet outil posera quelques questions nécessaires à la création du fichier de configuration Humbug ( humbug.json.dist
).
Dans le répertoire de base de votre projet, créez un fichier humbug.json.dist
:
{
"timeout" : 10 ,
"source" : {
"directories" : [
"src"
]
} ,
"logs" : {
"text" : "humbuglog.txt" ,
"json" : "humbuglog.json"
}
}
Vous pouvez valider le humbug.json.dist
dans votre VCS et le remplacer localement avec un fichier humbug.json
.
Modifiez le cas échéant. Si vous ne définissez pas au moins un journal, les informations détaillées sur les mutants échappés ne seront pas disponibles. Le journal texte est lisible par l’homme. Si des fichiers sources existent dans le répertoire de base ou si les fichiers des répertoires sources doivent être exclus, vous pouvez ajouter des modèles d'exclusion (en voici un pour les fichiers du répertoire de base où les répertoires Composer Vendor et Tests sont exclus) :
{
"timeout" : 10 ,
"source" : {
"directories" : [
"."
] ,
"excludes" : [
"vendor" ,
"Tests"
]
} ,
"logs" : {
"text" : "humbuglog.txt"
}
}
Si, à partir du répertoire de base de votre projet, vous devez exécuter des tests depuis un autre répertoire, vous pouvez également le signaler. Vous ne devriez pas avoir besoin d'exécuter Humbug à partir d'un répertoire autre que le répertoire de base de votre projet.
{
"chdir" : "tests" ,
"timeout" : 10 ,
"source" : {
"directories" : [
"src"
] ,
}
}
Assurez-vous que vos tests sont tous dans un état de réussite (les tests incomplets et ignorés sont autorisés). Humbug se fermera si l'un de vos tests échoue.
La commande magique, dans le répertoire de base de votre projet (en utilisant le téléchargement PHAR), est :
./humbug.phar
ou si vous venez de cloner Humbug :
../humbug/bin/humbug
ou si vous avez ajouté Humbug en tant que dépendance du compositeur à votre projet :
./vendor/bin/humbug
Au lieu de php avec l'extension xdebug, vous pouvez également exécuter Humbug via phpdbg :
phpdbg -qrr humbug.phar
Si tout s'est bien passé, vous obtiendrez quelque chose de similaire à :
_ _ _
| || |_ _ _ __ | |__ _ _ __ _
| __ | || | ' | '_ || / _` |
|_||_|_,_|_|_|_|_.__/_,___, |
|___/
Humbug version 1.0-dev
Humbug running test suite to generate logs and code coverage data...
361 [==========================================================] 28 secs
Humbug has completed the initial test run successfully.
Tests: 361 Line Coverage: 64.86%
Humbug is analysing source files...
Mutation Testing is commencing on 78 files...
(.: killed, M: escaped, S: uncovered, E: fatal error, T: timed out)
.....M.M..EMMMMMSSSSMMMMMSMMMMMSSSE.ESSSSSSSSSSSSSSSSSM..M.. | 60 ( 7/78)
...MM.ES..SSSSSSSSSS...MMM.MEMME.SSSS.............SSMMSSSSM. | 120 (12/78)
M.M.M...TT.M...T.MM....S.....SSS..M..SMMSM...........M...... | 180 (17/78)
MM...M...ESSSEM..MMM.M.MM...SSS.SS.M.SMMMMMMM..SMMMMS....... | 240 (24/78)
.........SMMMSMMMM.MM..M.SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS | 300 (26/78)
SSSSSSSSM..E....S......SS......M.SS..S..M...SSSSSSSS....MMM. | 360 (37/78)
.M....MM..SM..S..SSSSSSSS.EM.S.E.M............M.....M.SM.M.M | 420 (45/78)
..M....MMS...MMSSS................M.....EME....SEMS...SSSSSS | 480 (52/78)
SSSSS.EMSSSSM..M.MMMM...SSE.....MMM.M..MM..MSSSSSSSSSSSSSSSS | 540 (60/78)
SSS....SSSSSSSSMM.SSS..........S..M..MSSMS.SSSSSSSSSSSSSSSSS | 600 (68/78)
......E...M..........SM.....M..MMMMM.MMMMMSSSSSSSM.SS
653 mutations were generated:
284 mutants were killed
218 mutants were not covered by tests
131 covered mutants were not detected
17 fatal errors were encountered
3 time outs were encountered
Metrics:
Mutation Score Indicator (MSI): 47%
Mutation Code Coverage: 67%
Covered Code MSI: 70%
Remember that some mutants will inevitably be harmless (i.e. false positives).
Humbug results are being logged as JSON to: log.json
Humbug results are being logged as TEXT to: log.txt
Pour expliquer la sortie de progression peut-être énigmatique :
Les kills, les erreurs et les timeouts sont tous comptés comme des mutations détectées. Nous signalons des erreurs dans les journaux au cas où Humbug lui-même aurait rencontré une erreur interne, c'est-à-dire un bug à signaler comme problème ici !
L'exemple de résultats récapitulatifs a rapporté un certain nombre de scores de métriques :
Si vous examinez ces mesures, le problème le plus marquant est que le MSI de 47 % est inférieur de 18 points à la couverture de code déclarée à 65 %. Ces tests unitaires sont bien moins efficaces que ce que la couverture de code seule pourrait détecter.
L’interprétation de ces résultats nécessite un certain contexte. Les journaux répertorieront toutes les mutations non détectées en tant que différences par rapport au code source d'origine. Leur examen permettra de mieux comprendre quelles mutations spécifiques n’ont pas été détectées.
Humbug propose quelques options de ligne de commande intéressantes, autres que celles normalement associées à toute application Symfony Console.
Vous pouvez définir manuellement le seuil d'expiration pour n'importe quel test :
humbug --timeout=10
Si vous souhaitez uniquement muter un sous-ensemble de vos fichiers, vous pouvez transmettre n'importe quel nombre d'options --file
contenant des noms de fichiers simples, des globs ou des expressions régulières. Fondamentalement, tout cela est transmis à la méthode name()
du Symfony Finder.
humbug --file=NewClass.php --file= * Driver.php
Cela ne limite en aucun cas la vérification initiale de Humbug sur la suite de tests globale, qui est toujours exécutée dans son intégralité pour garantir que tous les tests réussissent correctement avant de continuer.
Si vous souhaitez muter uniquement quelques fichiers spécifiques, vous pouvez transmettre n'importe quel nombre d'options --path
contenant les noms de fichiers de chemin complet. Cette option sera transmise à un filtre Closure
qui croisera les fichiers trouvés à l'aide de l'option config et/ou --file
avec les fichiers fournis par vous à l'aide de l'option --path
.
humbug --path=src/Data/NewClass.php --path=src/Driver/Driver.php
Remarque : Cela ne limite en aucun cas la vérification initiale de Humbug sur la suite de tests globale, qui est toujours exécutée dans son intégralité pour garantir que tous les tests réussissent correctement avant de continuer.
L'analyse incrémentale (IA) est un mode de fonctionnement expérimental inachevé dans lequel les résultats sont mis en cache localement entre les exécutions et réutilisés là où cela a du sens. À l'heure actuelle, ce mode fonctionne de manière très naïve en éliminant les exécutions de tests dans lesquelles le fichier immédiat en cours de mutation et les tests pertinents pour une ligne mutée n'ont pas été modifiés depuis la dernière exécution (comme déterminé en comparant le SHA1 des fichiers impliqués).
humbug --incremental
Le mode IA offre une augmentation significative des performances pour des bases de code relativement stables, et vous êtes libre de le tester et de voir comment il se comporte dans la vie réelle. À l'avenir, il devra prendre en compte les modifications apportées aux fichiers contenant les classes parentes, les traits importés et les classes de ses dépendances immédiates, qui ont tous un impact sur le comportement d'un objet donné.
IA utilise un cache local permanent, par exemple /home/padraic/.humbug
.
Les tests de mutation sont traditionnellement lents. Le concept étant de réexécuter votre suite de tests pour chaque mutation générée. Pour accélérer les choses de manière significative, Humbug procède comme suit :
Bien que tout cela accélère Humbug, sachez qu'une exécution de Humbug sera plus lente qu'un test unitaire. Une suite de tests de 2 secondes peut nécessiter 30 secondes pour les tests de mutation. Ou 5 minutes. Tout dépend de l'interaction entre les lignes de code, du nombre de tests, du niveau de couverture du code et des performances du code et des tests.
Humbug implémente une suite de base de Mutateurs, qui nous indiquent essentiellement quand un jeton PHP particulier peut être muté, et applique également cette mutation à un tableau de jetons.
Remarque : Le code source contenu dans les fonctions (plutôt que dans les méthodes de classe) n'est pas muté pour le moment.
Arithmétique binaire :
Original | Muté | Original | Muté |
---|---|---|---|
+ | - | /= | *= |
- | + | %= | *= |
* | / | **= | /= |
/ | * | & | | |
% | * | | | & |
** | / | ^ | & |
+= | -= | ~ | |
-= | += | >> | << |
*= | /= | << | >> |
Substitution booléenne :
Cela englobe temporairement les mutateurs logiques.
Original | Muté |
---|---|
vrai | FAUX |
FAUX | vrai |
&& | || |
|| | && |
et | ou |
ou | et |
! |
Limites conditionnelles :
Original | Muté |
---|---|
> | >= |
< | <= |
>= | > |
<= | < |
Conditions niées :
Original | Muté | Original | Muté |
---|---|---|---|
== | != | > | <= |
!= | == | < | >= |
<> | == | >= | < |
=== | !== | <= | > |
!== | === |
Incréments :
Original | Muté |
---|---|
++ | -- |
-- | ++ |
Valeurs de retour :
Original | Muté | Original | Muté |
---|---|---|---|
renvoie vrai ; | renvoie faux ; | retourner 1.0> ; | retourner -( + 1); |
renvoie faux ; | renvoie vrai ; | renvoie $this ; | renvoie null ; |
renvoie 0 ; | renvoyer 1 ; | fonction de retour (); | fonction(); renvoie null ; |
retour ; | renvoie 0 ; | renvoyer une nouvelle classe ; | nouvelle classe ; renvoie null ; |
renvoie 0,0 ; | renvoyer 1,0 ; | return( Anything ); | ( Anything ); renvoie null ; |
renvoyer 1,0 ; | renvoie 0,0 ; |
Nombres littéraux :
Original | Muté |
---|---|
0 | 1 |
1 | 0 |
Int > 1 | Int + 1 |
Flottant >= 1 / <= 2 | Flotteur + 1 |
Flotteur > 2 | 1 |
Si les déclarations :
Toutes les instructions if sont largement couvertes par les mutateurs précédents, mais il existe des cas particuliers tels que l'utilisation de fonctions natives ou de méthodes de classe sans aucune comparaison ni opération, par exemple is_int()
ou in_array()
. Cela ne couvrirait pas les fonctions définies dans les fichiers puisqu'elles n'existent qu'au moment de l'exécution (autre chose sur lequel travailler !).
Original | Muté |
---|---|
si(is_int(1)) | si(!is_int(1)) |
Plus de Mutateurs seront ajoutés au fil du temps.
bin/humbug stats ../my-project/humbuglog.json ../my-project/list-of-classes.txt --skip-killed=yes [-vvv]
Analyse les statistiques de humbuglog.json ou de votre journal JSON nommé personnalisé.
Référence CLI :
humbug stats [humbuglog.json location] [class list location] [--skip-killed = yes] [-vvv]
humbuglog.json location, defaults to ./humbuglog.json
class list location, a path to a text file containing full class names, one per line.
only this files-related stats would be shown
--skip-killed=yes is used to completely skip output of "killed" section
various verbosity levels define amount of info to be displayed:
by default, there's one line per class with amount of mutants killed/escaped/errored/timed out (depending on output section)
-v adds one line per each mutant with line number and method name
-vv adds extra line for each mutant, displaying diff view of line mutant is detected in
-vvv shows full diff with several lines before and after
Cela peut être testé sur Humbug lui-même, en exécutant dans le répertoire de Humbug :
bin/humbug bin/humbug statistiques [-vvv]
Voici une courte liste de problèmes connus :
Avec l'aimable autorisation de Craig Davis qui a vu du potentiel dans un référentiel autrefois vide :P.
.:::::::::::...
.::::::::::::::::::::.
.::::::::::::::::::::::::.
::::::::::::::::::::::::::::.
::::::::::::::::::::::::::::::: .,uuu ...
:::::::::::::::::::::::::::::::: dHHHHHLdHHHHb
....:::::::'` ::::::::::::::::::' uHHHHHHHHHHHHHF
.uHHHHHHHHH' ::::::::::::::`. uHHHHHHHHHHHHHP"
HHHHHHHHHHH `:::::::::::',dHHuHHHHHHHHP".g@@g
J"HHHHHHHHHP 4H ::::::::' u$$$.
".HHHHHHHHP" .,uHP :::::' uHHHHHHHHHHP"",e$$$$$c
HHHHHHHF' dHHHHf `````.HHHHHHHHHHP",d$$$$$$$P%C
.dHHHP"" JHHHHbuuuu,JHHHHHHHHP",d$$$$$$$$$e=,z$$$$$$$$ee..
"" .HHHHHHHHHHHHHHHHHP",gdP" ..3$$$Jd$$$$$$$$$$$$$$e.
dHHHHHHHHHHHHHHP".edP " .zd$$$$$$$$$$$"3$$$$$$$$c
`???""??HHHHP",e$$F" .d$,?$$$$$$$$$$$$$F d$$$$$$$$F"
?be.eze$$$$$".d$$$$ $$$E$$$$P".,ede`?$$$$$$$$
4."?$$$$$$$ z$$$$$$ $$$$r.,.e ?$$$$ $$$$$$$$$
'$c "$$$$ .d$$$$$$$ 3$$$.$$$$ 4$$$ d$$$$P"`,,
"""- "$$".`$$" " $$f,d$$P".$$P zeee.zd$$$$$.
ze. .C$C"=^" ..$$$$$$P".$$$'e$$$$$P?$$$$$$
.e$$$$$$$"="$f",c,3eee$$$$$$$$P $$$P'd$$$$"..::.."?$%
4d$$$P d$$$dF.d$$$$$$$$$$$$$$$$f $$$ d$$$" :::::::::.
$$$$$$ d$$$$$ $$$$$$$$$$$$$$$$$$ J$$",$$$'.::::::::::::
"$$$$$$ ?$$$$ d$$$$$$$$$$$$$$$P".dP'e$$$$':::::::::::::::
4$$$$$$c $$$$b`$$$$$$$$$$$P"",e$$",$$$$$' ::::::::::::::::
' ?"?$$$b."$$$$.?$$$$$$P".e$$$$F,d$$$$$F ::::::::::::::::::
"?$$bc."$b.$$$$F z$$P?$$",$$$$$$$ ::::::::::::::::::::
`"$$c"?$$$".$$$)e$$F,$$$$$$$' ::::::::::::::::::::
':. "$b...d$$P4$$$",$$$$$$$" :::::::::::::::::::::
':::: "$$$$$".,"".d$$$$$$$F ::::::::::::::::::::::
:::: be."".d$$$4$$$$$$$$F :::::::::::::::::::::::
:::: "??$$$$$$$$$$?$P" :::::::::::::::::::::::::
:::::: ?$$$$$$$$f .::::::::::::::::::::::::::::
:::::::`"????"".::::::::::::::::::::::::::::::