Question

Cette question concerne l'utilisation de PHPUnit.

PHPUnit convertit automatiquement les erreurs php en exceptions. Existe-t-il un moyen de tester la valeur renvoyée par une méthode qui déclenche une erreur php (soit des erreurs intégrées, soit des erreurs générées par l'utilisateur via trigger_error )?

Exemple de code à tester:

function load_file ($file)
{
    if (! file_exists($file)) {
        trigger_error("file {$file} does not exist", E_USER_WARNING);
        return false;
    }
    return file_get_contents($file);
}

Voici le type de test que je veux écrire:

public function testLoadFile ()
{
    $this->assertFalse(load_file('/some/non-existent/file'));
}

Le problème que je rencontre est que l'erreur déclenchée provoque l'échec de mon test d'unité (comme il se doit). Mais si j'essaie de l'attraper ou de définir une exception attendue, tout code qui, après le déclenchement de l'erreur, ne s'exécute jamais ne s'exécute donc pas, je n'ai donc aucun moyen de tester la valeur renvoyée par la méthode.

Cet exemple ne fonctionne pas:

public function testLoadFile ()
{
    $this->setExpectedException('Exception');
    $result = load_file('/some/non-existent/file');

    // code after this point never gets executed

    $this->assertFalse($result);
}

Avez-vous des idées pour atteindre cet objectif?

Était-ce utile?

La solution

Il n’existe aucun moyen de le faire au sein d’un test unitaire. Cela est possible si vous divisez le test de la valeur de retour et la notification en deux tests différents.

Le gestionnaire d'erreurs de PHPUnit récupère les erreurs et les remarques PHP et les convertit en Exceptions - ce qui, par définition, arrête l'exécution du programme. La fonction que vous testez ne revient jamais du tout. Vous pouvez toutefois désactiver temporairement la conversion des erreurs en exceptions, même au moment de l'exécution.

C’est probablement plus facile avec un exemple, alors voici à quoi devraient ressembler les deux tests:

public function testLoadFileTriggersErrorWhenFileNotFound()
{
    $this->setExpectedException('PHPUnit_Framework_Error_Warning'); // Or whichever exception it is
    $result = load_file('/some/non-existent/file');

}

public function testLoadFileRetunsFalseWhenFileNotFound()
{
    PHPUnit_Framework_Error_Warning::$enabled = FALSE;
    $result = load_file('/some/non-existent/file');

    $this->assertFalse($result);
}

Cela présente également l'avantage supplémentaire de rendre vos tests plus clairs, plus propres et plus documentés.

Re: Commentaire: C'est une excellente question, et je n'avais aucune idée avant de faire quelques tests. Il semble que ne ne restaurez pas la valeur par défaut / originale, du moins à partir de PHPUnit 3.3.17 (la version stable actuelle).

Je voudrais donc modifier ce qui précède pour ressembler à ceci:

public function testLoadFileRetunsFalseWhenFileNotFound()
{
    $warningEnabledOrig = PHPUnit_Framework_Error_Warning::$enabled;
    PHPUnit_Framework_Error_Warning::$enabled = false;

    $result = load_file('/some/non-existent/file');

    $this->assertFalse($result);

    PHPUnit_Framework_Error_Warning::$enabled = $warningEnabledOrig;
}

Re: Deuxième commentaire:

Ce n'est pas tout à fait vrai. Je regarde le gestionnaire d’erreur de PHPUnit, et il fonctionne comme suit:

  • S'il s'agit d'un E_WARNING , utilisez PHPUnit_Framework_Error_Warning en tant que classe d'exception.
  • S'il s'agit d'une erreur E_NOTICE ou E_STRICT , utilisez PHPUnit_Framework_Error_Notice
  • Sinon, utilisez PHPUnit_Framework_Error en tant que classe d'exception.

Donc, oui, les erreurs du E_USER _ * ne sont pas transformées en classe * _Warning ou * _Notice de PHPUnit, elles sont toujours transformées en une exception générique PHPUnit_Framework_Error .

Réflexions complémentaires

Bien que cela dépende exactement de la manière dont la fonction est utilisée, je passerais probablement à la levée d'une exception réelle au lieu de déclencher une erreur, si c'était moi. Oui, cela modifierait le flux logique de la méthode et le code qui utilise la méthode ... pour le moment, l'exécution ne s'arrête pas quand il ne peut pas lire un fichier. Mais c'est à vous de décider si le fichier demandé qui n'existe pas est vraiment un comportement exceptionnel . J'ai tendance à utiliser les exceptions beaucoup plus que les erreurs / avertissements / avis, car elles sont plus faciles à gérer, à tester et à intégrer dans votre flux d'applications. Je réserve généralement les avis pour des choses comme les appels de méthode dépréciés, etc.

Autres conseils

Utilisez un fichier de configuration phpunit.xml et désactivez la conversion notice / avertissement / erreur en exception. Plus de des détails dans le manuel . C'est fondamentalement quelque chose comme ceci:

<phpunit convertErrorsToExceptions="false"
         convertNoticesToExceptions="false"
         convertWarningsToExceptions="false">
</phpunit>

Au lieu de s'attendre à un "générique exception " générique, pourquoi ne pas vous attendre à un " PHPUnit_Framework_Error " ?

Quelque chose comme cela pourrait faire:

/**
 * @expectedException PHPUnit_Framework_Error
 */
public function testFailingInclude()
{
    include 'not_existing_file.php';
}

Qui, je suppose, pourrait aussi s'écrire comme suit:

public function testLoadFile ()
{
    $this->setExpectedException('PHPUnit_Framework_Error');
    $result = load_file('/some/non-existent/file');

    // code after this point never gets executed

    $this->assertFalse($result);
}

Pour plus d'informations, voir Test des erreurs PHP
Surtout, il dit (citant):

  

PHPUnit_Framework_Error_Notice et    PHPUnit_Framework_Error_Warning représente   Avis PHP et avertissement, respectivement.


En regardant le fichier /usr/share/php/PHPUnit/TextUI/TestRunner.php présent sur mon système, je vois ceci (lignes 198 et suivantes) :

.
if (!$arguments['convertNoticesToExceptions']) {
    PHPUnit_Framework_Error_Notice::$enabled = FALSE;
}

if (!$arguments['convertWarningsToExceptions']) {
    PHPUnit_Framework_Error_Warning::$enabled = FALSE;
}

Alors peut-être devrez-vous passer un certain type de paramètre pour activer ce comportement? Mais il semble être activé par défaut ...

En fait, il existe un moyen de tester à la fois la valeur de retour et l’exception levée (dans ce cas, une erreur convertie par PHPUnit).

Il vous suffit de procéder comme suit:

public function testLoadFileTriggersErrorWhenFileNotFound()
{
    $this->assertFalse(@load_file('/some/non-existent/file'));

    $this->setExpectedException('PHPUnit_Framework_Error_Warning'); // Or whichever exception it is
    load_file('/some/non-existent/file');
}

Notez que pour tester la valeur de retour, vous devez utiliser l'opérateur de suppression d'erreur sur l'appel de la fonction ( @ avant le nom de la fonction). De cette façon, aucune exception ne sera lancée et l'exécution continuera. Vous devez ensuite définir l’exception attendue comme d’habitude pour tester l’erreur.

Vous ne pouvez pas tester plusieurs exceptions dans un test unitaire.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top