2008-10-24 11 views
17

je besoin d'ajouter des tests unitaires à des anciens scripts, les scripts sont essentiellement sous la forme suivante:Est-il possible d'utiliser ou de requérir un script Perl sans exécuter ses instructions?

#!/usr/bin/perl 

# Main code 
foo(); 
bar(); 

# subs 
sub foo { 

} 
sub bar { 

} 

Si j'essaie de « besoin » ce code dans un test unitaire, la section principale du code va courir, où que je veux être en mesure de tester "foo" dans l'isolement.

Y at-il un moyen de le faire sans déplacer foo, bar dans un fichier .pm séparé?

Répondre

16

Une autre astuce courante pour les scripts de tests unitaires est d'envelopper le corps de leur code dans un bloc « appelant »:

#!/usr/bin/perl 

use strict; 
use warnings; 

unless (caller) { 
    # startup code 
} 

sub foo { ... } 

Lorsqu'il est exécuté à partir de la ligne de commande, Cron, un script bash, etc., il fonctionne normalement. Cependant, si vous le chargez à partir d'un autre programme Perl, le code "unless (caller) {...}" ne s'exécute pas. Ensuite, dans votre programme de test, déclarez un espace de noms (puisque le script exécute probablement le code dans le paquet principal :) et «faites» le script.

#!/usr/bin/perl 

package Tests::Script; # avoid the Test:: namespace to avoid conflicts 
         # with testing modules 
use strict; 
use warnings; 

do 'some_script' or die "Cannot (do 'some_script'): $!"; 

# write your tests 

'faire' est plus efficace qu'éval et assez propre pour cela.

Une autre astuce pour tester les scripts consiste à utiliser Expect. C'est plus propre, mais il est également plus difficile à utiliser et il ne vous laissera rien remplacer dans le script si vous avez besoin de se moquer de quelque chose.

+0

S'il y a une possibilité à distance que quelqu'un empaquette votre script avec PAR (ie "pp -o binary script.pl; ./binary") ou l'évalue réellement et s'attende à ce qu'il s'exécute, alors * s'il vous plaît * ne le faites pas . Cela m'a donné beaucoup de soucis avec perlcritic quand je préparais mon discours YAPC :: EU. – tsee

+0

Pour être juste, je ne m'inquiète pas au sujet de PAR. Perl 5 a trop de défauts pour que je me souvienne de tous les cas spéciaux, en particulier pour le code non-core :( – Ovid

+0

@tsee sur quelle fin est votre préoccupation sur? Écrire des scripts dans un bloc 'à moins appelant 'ou exécuter le test par' – ajwood

17

En supposant que vous avez pas de problèmes de sécurité, l'envelopper dans un sous {...} et eval il:

use File::Slurp "read_file"; 
eval "package Script; sub {" . read_file("script") . "}"; 

is(Script::foo(), "foo"); 

(en prenant soin que la eval n'est pas portée des lexicales qui seraient fermés par le script).

+1

Puisqu'il faut exécuter du code pour le tester, il n'y a pas de problème de sécurité particulier avec l'utilisation d'eval STRING ici. – Schwern

10

Ahh, l'ancienne question "comment puis-je tester un programme". L'astuce la plus simple est de mettre dans votre programme avant de commencer à faire les choses:

return 1 unless $0 eq __FILE__; 

__FILE__ est le fichier source de courant. $0 est le nom du programme en cours d'exécution. Si elles sont identiques, votre code est exécuté en tant que programme. Si elles sont différentes, elles sont chargées en tant que bibliothèque. Cela suffit pour vous permettre de commencer à tester les sous-programmes à l'intérieur de votre programme.

require "some/program"; 
...and test... 

L'étape suivante consiste à déplacer tout le code en dehors d'un sous-programme dans main, alors vous pouvez le faire:

main() if $0 eq __FILE__; 

et vous pouvez maintenant tester main() comme tout autre sous-programme. Une fois cela fait, vous pouvez commencer à envisager de déplacer les sous-programmes du programme dans leurs propres bibliothèques réelles.

+0

La même chose que je signale dans le commentaire de la solution d'Ovid s'applique ici: Essayez de l'empaqueter avec PAR :: Packer ou des outils similaires – tsee

+0

@tsee Je ferai sauter ce pont quand il viendra. intelligent et semble contourner le problème sans avoir à modifier le code du tout. – Schwern