TESTS UNITAIRES

pierreSabatier Messages postés 27 Date d'inscription lundi 12 avril 2010 Statut Membre Dernière intervention 15 octobre 2011 - 7 oct. 2011 à 18:53
pierreSabatier Messages postés 27 Date d'inscription lundi 12 avril 2010 Statut Membre Dernière intervention 15 octobre 2011 - 25 oct. 2011 à 03:47
Cette discussion concerne un article du site. Pour la consulter dans son contexte d'origine, cliquez sur le lien ci-dessous.

https://codes-sources.commentcamarche.net/source/53655-tests-unitaires

pierreSabatier Messages postés 27 Date d'inscription lundi 12 avril 2010 Statut Membre Dernière intervention 15 octobre 2011 1
25 oct. 2011 à 03:47
Suite aux sollicitations publiques et privees, je vais faire mon possible pour ameliorer le packaging de mon code.
Sortie possible dans deux semaines (je n ai pas d ordi, utilise un qwerty pourri dans un web bar, etc).

Votre tres oblige ;-)
jpaul078 Messages postés 3 Date d'inscription dimanche 3 avril 2005 Statut Membre Dernière intervention 20 octobre 2011
20 oct. 2011 à 06:57
Bonjour,

Dans la première réponse que tu m'avais faite, j'avais bien vu que tu faisais allusion à cette fonction launch mais je pensais que tu l'avais intégrée à ton fichier de livraison et je l'ai donc retéléchargé à l'aide du lien 'Télécharger le zip'...
Comment fait-on pour télécharger ta dernière version ?

Donc je viens de la rajouter (par copier coller de la fonction que tu as donnée dans ta réponse d'hier soir) juste après la fonction __destruct() dans le fichier test/testUnitaires.php et ça ne change pas grand chose.
L'autre souci, c'est que si je change un des tests pour qu'il soit fau par exemple :
$mon_adr->longueur_dom('toto@truc.fr') == 5
au lieu de :
$mon_adr->longueur_dom('toto@truc.fr') == 7

La seule différence (qui ne saute pas aux yeux) c'est la dernière ligne :
48 test(s) lancé(s). OK
au lieu de :
49 test(s) lancé(s). OK

J'ai encore dû merder quelque part...

En tous cas, merci de partager ce travail :)
Jean-Paul
pierreSabatier Messages postés 27 Date d'inscription lundi 12 avril 2010 Statut Membre Dernière intervention 15 octobre 2011 1
19 oct. 2011 à 23:04
Oui, je peux te le dire ;-)

Vérifie ta classe mère "TestUnitaire",car si j'ai bien compris ton erreur, la méthode launch() ne doit pas figurer. Or, il faut, dans l'état actuel de ton code, qu'elle fasse quelque chose comme :

final public function launch(){
foreach (get_class_methods($this) as $m) {
// ... qu'il faut appeler par CONVENTION
if (substr($m, 0, 5) == 'test_') {
// ... je les appelle dans un environnement clean
$this->setUp();
$this->$m();
$this->tearDown();
}
}
}

Dans le cas de ton erreur, __call est appelé récursivement... Je prends bonne note de ce bug et vais le corriger avec ce test dans la fonction __call
if (method_exists($this,$method)){
call_user_func_array(array($this, $method), $args);
}

Merci pour ta remontée d'erreur ! Tiens-moi au courant pour ton code.

PS : une nouvelle mouture va voir le jour. Elle sera (rétro-)compatible avec tes classes ;-)
jpaul078 Messages postés 3 Date d'inscription dimanche 3 avril 2005 Statut Membre Dernière intervention 20 octobre 2011
19 oct. 2011 à 22:41
Bonjour ou plutôt bonsoir,

Je suis désolé mais je dois passer à côté de quelque chose : même avec tes explications, je n'arrive pas à comprendre.
Faut dire que je n'ai pas trop de temps et que je n'ai pas investi en regarder ton code...
Je me suis fait un répertoire exemple avec la classe à tester suivante (fichier=class_verifadr.php)
----------------- début code -----------------
<?php
class verifadr
{
function longueur_nom($adr)
{
return strlen(substr($adr,0,strpos($adr, '@')));
}
function longueur_dom($adr)
{
return strlen(substr($adr,strpos($adr, '@')+1));
}
}
?>
----------------- fin code -----------------
j'ai téléchargé le zip et j'ai copié le dossier test dans mon dossier exemple.

Maintenant, il faut que je crée mon fichier de tests (fichier=test_verifadr.php) et c'est là que je me demande ce qu'il faut faire...
J'imagine qu'il faut que je charge ma classe et la classe de tests unitaires donc je suis arrivé à un truc comme :
----------------- début code -----------------
<?php
require('test/testUnitaire.php');
require('class_verifadr.php');

class verifadrTest extends TestUnitaire
{

function test_alpha()
{
$mon_adr=new verifadr();
if ($mon_adr->longueur_nom('toto@truc.fr') == 4)
{
return true;
}
else
{
return false;
}
}
function test_beta()
{
$mon_adr=new verifadr();
if ($mon_adr->longueur_dom('toto@truc.fr') == 7)
{
return true;
}
else
{
return false;
}
}
}

$obj = new verifadrTest();
$obj->launch();
?>
----------------- fin code -----------------
quand je charge le fichier test_verifadr.php dans mon navigateur j'ai :
1) un warning :
Warning: Cannot modify header information
2) ça affiche :
Début des tests
3) ensuite j'ai une erreur :
Fatal error: Maximum function nesting level of '100' reached, aborting! in /home/jpaul/workspace/tests/testUnitaire/exemple/test/testUnitaire.php on line 47
4) un tableau de 99 lignes auquel je ne comprends pas grand chose voici la ligne de titre et la première et la dernière lignes :
# Time Memory Function Location
1 0.0004 337560 {main}( ) ../test_verifadr.php:0
99 0.0039 443628 TestUnitaire->__call( ) ../testUnitaire.php:0
5) une ligne qui semble être le résultat :
49 test(s) lancé(s). OK

Peux-tu me dire ce que j'ai fait de travers ?

Cordialement,
Jean-Paul OLIVIER
pierreSabatier Messages postés 27 Date d'inscription lundi 12 avril 2010 Statut Membre Dernière intervention 15 octobre 2011 1
19 oct. 2011 à 11:35
Merci Akh, c'est grâce à toi que j'améliore ce source.

En fin de semaine, je déménage. Mais je ne vais pas oublier de packager ce framework. Il faudra un peu plus de patience, voilà tout.
cs_aKheNathOn Messages postés 575 Date d'inscription dimanche 23 décembre 2001 Statut Membre Dernière intervention 23 octobre 2012
17 oct. 2011 à 17:54
C'est du joli travail, j'adhère vraiment de plus en plus à ton mini-framework !

Quelques petites choses supplémentaires histoire de l'améliorer :

Dans la classe TestUnitaire, il te manque une fonction fail($message) qui permet de forcer le log à afficher un statut de fail sur le test en cours. C'est utilisé si on sort d'un simple assert :

if ($cond1 != $val && ($cond2 > $max || $cond2 < $min) fail("valeur aux limites);

D'ailleurs tu peux aussi lors de la génération des classes la rajouter (dans TestManager --> write_method : dans le corp de la méthode rajoutes :
$this->fail('Not implemented yet');

Dans le TestManager toujours, dans la méthode launch, fais un try catch sur l’exécution du test car les erreurs peuvent faire arrêter le test sinon, à logguer aussi dans le statut d’exécution.

Il ne manque plus grand chose à ta librairie, juste le packaging : un nom de projet, de la phpdoc sur les classes, un repository SVN et un site web (sourceforge est pas mal pour ce genre de choses). Si tu le mets sous GIT (https://github.com/) je vais peut-être en faire un fork :)

En tout cas merci de partager ton source, je vais surement l'utiliser :)

Bonne prog et tiens-nous au courant,
akh
pierreSabatier Messages postés 27 Date d'inscription lundi 12 avril 2010 Statut Membre Dernière intervention 15 octobre 2011 1
17 oct. 2011 à 14:01
Toujours pas de nouveau commentaire ?
pierreSabatier Messages postés 27 Date d'inscription lundi 12 avril 2010 Statut Membre Dernière intervention 15 octobre 2011 1
12 oct. 2011 à 18:06
Bon, voilà un remaniement complet, qui ne sera probablement pas du goût de tous. Toutes vos remarques sont les bienvenues :-)
pierreSabatier Messages postés 27 Date d'inscription lundi 12 avril 2010 Statut Membre Dernière intervention 15 octobre 2011 1
11 oct. 2011 à 09:25
Salut JPAUL,

Voilà comment aujourd'hui, ce "framework" de test fonctionne. Ta classe de test hérite de TestUnitaire. Les méthodes de tests doivent commencer par "test_".

<?php class LambdaTest extends TestUnitaire {

function test_alpha(){}
function test_beta(){}
}

$obj = new LambdaTest();
$obj->launch();

Ps : j'ai rajouté la méthode launch() dans TestUnitaire. Elle contient :
foreach (get_class_methods($this) as $m) {
// ... qu'il faut appeler par CONVENTION
if (substr($m, 0, 5) == 'test_') {
// ... je les appelle dans un environnement clean
$this->setUp();
$this->$m();
$this->tearDown();
}
}

Voilà la version alpha de ce "framework". Les modifs vont arriver quand je peux.
jpaul078 Messages postés 3 Date d'inscription dimanche 3 avril 2005 Statut Membre Dernière intervention 20 octobre 2011
11 oct. 2011 à 06:48
Bonjour,

L'idée des tests unitaires m'intéresse énormément.
Ce code fait appel à des notions que je n'ai pas l'habitude d'utiliser en php.
De ce fait, cela m'intéresse encore plus.
Mais j'ai fait un test sans succès et avant de pouvoir le faire fonctionner, j'ai peur qu'il ne me faille investir pas mal de temps pour éclaircir les notions qui m'échappent.
J'ai peu de temps de temps en ce moment et je pense qu'un petit exemple d'utilisation m'aiderais beaucoup.

Dans tout les cas merci de partager ton travail.

Cordialement,
Jean-Paul
cs_aKheNathOn Messages postés 575 Date d'inscription dimanche 23 décembre 2001 Statut Membre Dernière intervention 23 octobre 2012
10 oct. 2011 à 17:33
C'est très bien, pour les setup et teardown - ça permet de se créer un contexte de tests ISO et mieux cloisonnés - important pour éviter les effets de bords de certains tests - petit bémol quand tu sort du scope du $this le cloisonnement n'est plus géré ($_REQUEST, $_SESSION, $GLOBALS ... appels vers des variables statiques) - mais ce type d'effet de bord est très minime.

Parcontre je ne mettrait pas les appels (setUp, test_..., tearDown) dans l'instanciation de la classe de test mais je ferais une classe TestManager à part, qui elle lencerait par exemple les tests sur une classe, ou sur un ensemble de classes. L'objet devrait à mon sens être reconstruit à chaque execution de test.

La classe de log c'est bien la class Débug que tu as actuellement, elle devrait effectivement être en mode singleton et juste stocker les infos.

La classe TestManager s'occuperait de dumper les résultats en fin de test par exemple. Pour la structure de débug, faudrait rajouter un niveau de log sur la classe en cours d'execution vu que c'est multi-classes.

J'utiliserais les classes de Reflection dans le TestManager pour gérer les annotations de documentation, histoire d'associer des commentaires aux fonctions testées et de les affichers dans les résultats.

NB : Un truc utile dans le TestManager est de pouvoir générer un fichier de test unitaire en lui passant en argument un nom de classe.

Si tu peux faire le tout en moins de 1000 lignes de codes, tu tiens un framework de tests unitaires complet et surtout facile d'utilisation.

:) bon courrage
pierreSabatier Messages postés 27 Date d'inscription lundi 12 avril 2010 Statut Membre Dernière intervention 15 octobre 2011 1
10 oct. 2011 à 16:16
Akhenaton, j'ai supprimé le côté brouillon de mon source.

En suivant tes indications, je développe un nouveau code. Je pense à 4 classes (ces deux-là, une de logs complet et une d'affichage).

Dans le construct de la classe mère "testUnitaire", je pourrais écrire quelque chose comme :
// Pour chacune de mes méthodes ...
foreach (get_class_methods($this) as $m) {
// ... qu'il faut appeler par CONVENTION
if (substr($m, 0, 5) == 'test_') {
// ... je les appelle dans un environnement clean
$this->setUp();
$this->$m();
$this->tearDown();
}
}

Est-ce que je vois juste ?
cs_aKheNathOn Messages postés 575 Date d'inscription dimanche 23 décembre 2001 Statut Membre Dernière intervention 23 octobre 2012
10 oct. 2011 à 14:50
Bonjour Pierre,

Un source très intéressant, les asserts en PHP sont assez peu utilisés donc c'est très instructif d'en voire un exemple.

Le code est très simple et j'aime ton approche KISS et justifiant le fait de ne pas passer par un framework de type PHPUnit.

Je commence par un point "obscur" sur le :

$this->_return[$this->iNbError][$config] = $$config; la liste des clés étant limitée par la signature de la fonction je ne vois pas trop l'intérêt de cette boucle.

Un simple code aurait pû suffire :

$this->_return[] = array(
'line' => $line,
'code' => $code
);

--> Pour faire un peu d'optimisations :) <--

Dans la classe Debug :

- Les variables iNbError, et _config n'ont pas besoin d'exister
A la place de iNbError, tu peux utiliser : sizeof($this->_return)

- De la manière dont tu as écrit ta classe Debug n'est pas un singleton, donc soit supprimer oInstance soit en faire un vrai : cf partie PHP 5 : http://fr.wikipedia.org/wiki/Singleton_%28patron_de_conception%29

Dans la classe TestUnitaire :

- J'aurais plutôt mis l'instanciation des asserts dans les DEBUG

- Pour permettre d'instancier les asserts à partir des DEBUG j'aurais éventuellement loggué dans Debug toutes les assertions, ainsi j'aurais également eu le log de tout ce qui est OK.

Sur le principe, il me manque un élément, c'est le contexte d’implémentation des tests unitaires : je présume qu'actuellement on doit le fait en bloc dans le construct. Au passage dans la classe TestUnitaire ton __construct devrait être final public pour éviter les effets de bords de l'héritage.

* Y'à t'il moyen d'avoir un cloisonnement des contextes avec plusieurs fonctions ...
* Si dans une exécution on veut exécuter plusieurs classes de tests à la fois ...

Si tu règle ces petits points je pense que ta classe à vraiment un bon potentiel car c'est pas vraiment intéressant de déployer, configurer et customiser PHPUnit, alors que ton code serait beaucoup plus facilement adaptable.

Bonne continuation,
akh
pierreSabatier Messages postés 27 Date d'inscription lundi 12 avril 2010 Statut Membre Dernière intervention 15 octobre 2011 1
7 oct. 2011 à 18:53
Bonsoir tout le monde,

La source a été vue plus de 180 fois. Quelqu'un voudrait-il formuler un petit commentaire ?
Rejoignez-nous