Phpunit: La sérialisation de la «fermeture» n'est pas autorisée

Créé le 2 janv. 2012  ·  12Commentaires  ·  Source: sebastianbergmann/phpunit

Bien que les fermetures $GLOBAL ne soient plus sérialisées depuis 01aa34783c9b19d39e568641f3f2dd0fc983b11a & #352, un tableau $GLOBAL qui contient une fermeture comme valeur provoquera toujours l'erreur "La sérialisation de la fermeture n'est pas autorisée".

$GLOBALS[] = array( 'foo' => function() { return 'bar'; }  );
typbug

Commentaire le plus utile

J'ai ce même message d'erreur chaque fois que j'instancie un new MyRestClient() , dont le constructeur contient $this->client = new \GuzzleHttp\Client(); . C'est également la même chose lorsque j'ai essayé d'utiliser un $x=function(){}; dans le constructeur de ma classe. Je suppose que le client Guzzle fait quelque chose de similaire en interne. Cela rend impossible l'instanciation à partir d'un test phpunit. Je suis tellement triste maintenant :sanglot:

Tous les 12 commentaires

Un autre cas, je l'obtiens

 /* test.php */
 clotureTest de classe étend PHPUnit_Framework_TestCase
 {
 fonction testFermeture() {
 $this->assertEquals(1, 1);
 call_user_func(function($a) {
 lancer un nouveau \Exception("test");
 }, dix);
 $this->assertEquals(2, 2);
 }
 }

puis exécutez-le dans l'isolement du processus

 phpunit --process-isolation test.php

tu obtiendrais

 Il y a eu 1 erreur :
 1) phTest::testFermeture
 PHPUnit_Framework_Exception : Erreur fatale PHP : Exception non détectée 'Exception' avec le message 'La sérialisation de 'Closure' n'est pas autorisée' dans -:37
 Trace de la pile:
 #0 -(37): sérialiser (tableau)
 #1 -(123): __phpunit_run_isolated_test()
 #2 {principal}
 jeté dedans - à la ligne 37
 LES ÉCHECS!
 Tests : 1, Assertions : 0, Erreurs : 1.

J'avais le même problème lors de l'utilisation de l'isolation de processus, pour le contourner, j'ai ajouté __sleep à mon testCase et nettoyé les fermetures à l'intérieur. Pour moi, j'avais stocké des maquettes dans les propriétés du test, donc c'était vraiment de ma faute, j'espère que cela aidera quelqu'un.

J'ai également le même problème et je peux reproduire le problème @tmilos mentionné précédemment. Je ne sais pas si ce ticket est mort, mais ce serait bien si nous pouvions résoudre ce problème. Je suis plus que disposé à faire une pull request si c'est préférable.

Les demandes de tirage sont toujours appréciées !

Je sais que ça fait un peu, mais j'étais hors projet pendant un moment. Suite au commentaire de @tmilos , je peux reproduire le problème si je force une erreur de syntaxe, par exemple :

/* test.php */
class closureTest extends PHPUnit_Framework_TestCase
{
    function testClosure() {
        call_user_func(function($a) {
            throw new \Exception("test");
        });
    }
}

Notez l'absence d'argument fourni à la fin de call_user_func . L'exception de sérialisation résulte de la tentative de sérialisation de l'objet PHPUnit_Framework_TestResult. Cette tentative de sérialisation se trouve à la ligne 42 de PHPUnit/Framework/Process/TestCaseMethod.tpl.dist . La partie incriminée de l'objet à sérialiser est une référence de fermeture dans la hiérarchie de trace de l'exception qui a été levée :

[1] => Array
    (
        [function] => {closure}
        [class] => Issue451Test
        [type] => ->
        [args] => Array
            (
            )

)

Ma question alors, comme c'est la première fois que je corrige PHPUnit, ne consiste pas à supprimer la fermeture ou à la modifier pour qu'elle soit par ailleurs bénigne - ce qui impliquerait sans aucun doute l'utilisation de Reflection - existe-t-il un moyen PHPUnit de meilleure pratique pour gérer cela ? Des idées ou des commentaires au moins ?

De plus, j'ai écrit un test sous Regressions/GitHub. Est-ce acceptable ?

@kunjalpopat , en référence à votre problème, la première chose que je vérifierais est les exceptions. Lors de l'isolement du processus, les exceptions levées peuvent vous donner un message « Sérialisation de la fermeture ». Essayez d'envelopper votre code de test dans un try/catch et voyez si cela résout le problème, si c'est le cas, vous voudrez peut-être réduire la couverture du try/catch jusqu'à ce que vous trouviez la zone à problème.

Le problème réside dans PHPUnit_Util_GlobalState::backupGlobals :

if ($key != 'GLOBALS' &&
    !in_array($key, $superGlobalArrays) &&
    !in_array($key, $blacklist) &&
    !$GLOBALS[$key] instanceof Closure) {  // <-- this is the problem
    self::$globals['GLOBALS'][$key] = serialize($GLOBALS[$key]);
}

J'ai inventé une aide en PHPUnit_Util_GlobalState :

public static function checkIfThereIsClosureInIt($arr) {
    if ($arr instanceof Closure)
        return true;
    if (is_object($arr))
        $arr = get_object_vars($arr);
    if (is_array($arr))
        foreach ($arr as $x)
            if (PHPUnit_Util_GlobalState::checkIfThereIsClosureInIt($x))
                return true;
    return false;
}

et modifié une petite fonction backupGlobals :

foreach (array_keys($GLOBALS) as $key) {
    if ($key != 'GLOBALS' &&
        !in_array($key, $superGlobalArrays) &&
        !in_array($key, $blacklist) &&
        !PHPUnit_Util_GlobalState::checkIfThereIsClosureInIt($GLOBALS[$key])
//        !$GLOBALS[$key] instanceof Closure
    ) {
        self::$globals['GLOBALS'][$key] = serialize($GLOBALS[$key]);
    }
}

cette version semble fonctionner (je n'attrape plus cette exception).
Comme il s'agit d'une récursivité contre $GLOBALS, cela peut donc donner un peu plus de chaleur au processeur.

Le correctif de code ci-dessus de stafr a résolu les problèmes PHPunit que je rencontrais avec les exceptions "Sérialisation de la fermeture". Merci beaucoup!

J'ai ce même message d'erreur chaque fois que j'instancie un new MyRestClient() , dont le constructeur contient $this->client = new \GuzzleHttp\Client(); . C'est également la même chose lorsque j'ai essayé d'utiliser un $x=function(){}; dans le constructeur de ma classe. Je suppose que le client Guzzle fait quelque chose de similaire en interne. Cela rend impossible l'instanciation à partir d'un test phpunit. Je suis tellement triste maintenant :sanglot:

J'ajoute juste mon expérience. Fondamentalement, chaque fois que vous mettez une fonction ou une classe anonyme comme attribut de classe de test, phpunit lancera Serialization of 'Closure' is not allowed ou Serialization of 'class<strong i="6">@anonymous</strong>' is not allowed . Donc, si vous devez utiliser une fonction ou une classe anonyme, assurez-vous qu'il s'agit d'une variable unique, et non d'un attribut de classe de test.

Cette page vous a été utile?
0 / 5 - 0 notes

Questions connexes

rentalhost picture rentalhost  ·  4Commentaires

keradus picture keradus  ·  4Commentaires

nicklevett picture nicklevett  ·  4Commentaires

klesun picture klesun  ·  4Commentaires

keradus picture keradus  ·  3Commentaires