Comment écrire des tests unitaires en PHP avec une base de code procédurale?

Je suis surtout convaincu des avantages des tests unitaires, et je voudrais commencer à appliquer le concept à une grande base de code existante écrite en PHP. Moins de 10% de ce code est orienté objet.

J'ai regardé plusieurs frameworks de tests unitaires (PHPUnit, SimpleTest et phpt). Cependant, je n'ai trouvé aucun exemple pour l'un d'entre eux qui teste le code de procédure. Quel est le meilleur cadre pour ma situation et y a-t-il des exemples de tests unitaires PHP utilisant du code non-POO?

29
demandé sur Travis Beale 2009-05-22 22:46:05

3 réponses

Vous pouvez tester à L'unité PHP procédural, pas de problème. Et vous n'êtes certainement pas hors de la chance si votre code est mélangé avec HTML.

Au niveau de l'application ou du test d'acceptation, votre php procédural dépend probablement de la valeur des superglobales ($_POST, $_GET, $_COOKIE, etc.) pour déterminer le comportement, et se termine en incluant un fichier de modèle et en crachant la sortie.

Pour faire des tests au niveau de l'application, vous pouvez simplement définir les valeurs superglobales; démarrer un tampon de sortie (pour garder un tas de html à partir de inonder votre écran); appeler la page; affirmer contre des choses à l'intérieur du tampon; et corbeille le tampon à la fin. Donc, vous pouvez faire quelque chose comme ceci:

public function setUp()
{
    if (isset($_POST['foo'])) {
        unset($_POST['foo']);
    }
}

public function testSomeKindOfAcceptanceTest()
{
    $_POST['foo'] = 'bar';
    ob_start();
    include('fileToTest.php');
    $output = ob_get_flush();
    $this->assertContains($someExpectedString, $output);
}

Même pour d'énormes "frameworks" avec beaucoup d'inclusions, ce type de test vous dira si vous avez des fonctionnalités au niveau de l'application qui fonctionnent ou non. Cela va être très important lorsque vous commencez à améliorer votre code, car même si vous êtes convaincu que le connecteur de base de données fonctionne toujours et semble mieux qu'avant, vous voudrez pour cliquer sur un bouton et voir cela, oui, vous pouvez toujours vous connecter et vous déconnecter via la base de données.

Aux niveaux inférieurs, il y a des variations mineures selon la portée de la variable et si les fonctions fonctionnent par des effets secondaires (renvoyer true ou false), ou renvoyer le résultat directement.

Les variables sont-elles transmises explicitement, en tant que paramètres ou tableaux de paramètres entre les fonctions? Ou les variables sont-elles définies dans de nombreux endroits différents et transmises implicitement en tant que globales? Si c'est le (bon) cas explicite, vous pouvez tester une fonction par Unité (1) en incluant le fichier contenant la fonction, puis (2) en alimentant directement les valeurs de test de fonction, et (3) en capturant la sortie et en l'affirmant. Si vous utilisez des globals, il vous suffit d'être très prudent (comme ci-dessus, dans l'exemple $_POST) pour annuler soigneusement tous les globals entre les tests. Il est également particulièrement utile de garder les tests très petits (5-10 lignes, 1-2 assertions) lorsqu'il s'agit d'une fonction qui pousse et tire beaucoup de globals.

Un autre le problème de base est de savoir si les fonctions fonctionnent en retournant la sortie, ou en modifiant les paramètres passés, en retournant true/false à la place. Dans le premier cas, les tests sont plus faciles, mais encore une fois, c'est possible dans les deux cas:

// assuming you required the file of interest at the top of the test file
public function testShouldConcatenateTwoStringsAndReturnResult()
{
  $stringOne = 'foo';
  $stringTwo = 'bar';
  $expectedOutput = 'foobar';
  $output = myCustomCatFunction($stringOne, $stringTwo);
  $this->assertEquals($expectedOutput, $output);
}

Dans le mauvais cas, si votre code fonctionne par effets secondaires et renvoie true ou false, vous pouvez toujours tester assez facilement:

/* suppose your cat function stupidly 
 * overwrites the first parameter
 * with the result of concatenation, 
 * as an admittedly contrived example 
 */
public function testShouldConcatenateTwoStringsAndReturnTrue()
    {
      $stringOne = 'foo';
      $stringTwo = 'bar';
      $expectedOutput = 'foobar';
      $output = myCustomCatFunction($stringOne, $stringTwo);
      $this->assertTrue($output);
      $this->Equals($expectedOutput, $stringOne);
    }

J'espère que cela aide.

35
répondu jared 2009-06-18 17:07:26

Ce que les tests unitaires à bien faire, et ce que vous devriez utiliser, c'est quand vous avez un morceau de code que vous donner un certain nombre d'entrées, et vous vous attendez à obtenir un certain nombre de sorties de retour. L'idée étant, lorsque vous ajoutez des fonctionnalités plus tard, vous pouvez exécuter vos tests et vous assurer qu'il exécute toujours l'ancienne fonctionnalité de la même manière.

Donc, si vous avez une base de code procédurale, vous pouvez accomplir cela en appelant vos fonctions dans les méthodes de test

require 'my-libraries.php';
class SomeTest extends SomeBaseTestFromSomeFramework {
    public function testSetup() {
        $this->assertTrue(true);
    }

    public function testMyFunction() {
        $output = my_function('foo',3);

        $this->assertEquals('expected output',$output);
    }
}

Cette astuce avec le code PHP bases est, souvent, votre code de bibliothèque interférera avec l'exécution de votre framework de test, car votre base de code et les frameworks de test auront beaucoup de code lié à la configuration d'un environnement d'application dans un navigateur web (session, variables globales partagées, etc.). Attendez-vous à passer du temps à arriver à un point où vous pouvez inclure votre code de bibliothèque et exécuter un test simple (la fonction testSetup ci-dessus).

Si votre code n'a pas de fonctions, et est juste une série de fichiers PHP qui sortie des pages HTML, vous êtes un peu hors de la chance. Votre code ne peut pas être séparé en unités distinctes, ce qui signifie que les tests unitaires ne vous seront pas d'une grande utilité. Vous feriez mieux de passer votre temps au niveau des "tests d'acceptation" avec des produits comme Selenium et Watir. Ceux-ci vous permettront d'automatiser un navigateur, puis de vérifier le contenu des pages en tant qu'emplacements spécifiques/dans les formulaires.

6
répondu Alan Storm 2009-05-22 20:09:09

Vous pouvez essayer d'inclure votre code non-poo dans une classe de test en utilisant

require_once 'your_non_oop_file.php' # Contains fct_to_test()

Et avec phpUnit vous définissez votre fonction de test:

testfct_to_test() {
   assertEquals( result_expected, fct_to_test(), 'Fail with fct_to_test' );
}
1
répondu Luc M 2009-05-22 18:58:53