Que et quand faut-il tester ?

a marqué ce sujet comme résolu.

Bonjour, je me pose plusieurs questions concernant les tests unitaires.

Exemple simple

Sur une méthode comme celle-ci :

  /**
   * Return the value of an environment variable
   * 
   * @param  $name     name of the environment variable
   * @param  $default  value to return if the specified environment variable does not exist
   */
  public static function getEnv(string $name, mixed $default = null): mixed
  {
    $name = strtoupper($name);
    return array_key_exists($name, $_ENV) ? $_ENV[$name] : $default;
  }

j’effectue les tests suivants :

<?php

declare(strict_types=1);

use
  PHPUnit\Framework\TestCase,
  App\App;

final class AppTest extends TestCase
{
  protected function setUp(): void
  {
    App::loadEnv();
  }

  public function testGetEnv(): void
  {
    $env = App::getEnv('app_name');
    $this->assertSame('My Wirework App', $env);
  }

  public function testGetEnvNull(): void
  {
    $env = App::getEnv('does_not_exist');
    $this->assertSame(null, $env);
  }

  public function testGetEnvDefault(): void
  {
    $env = App::getEnv('does_not_exist', 42);
    $this->assertSame(42, $env);
  }

Ceci-dit, faut-il tester les erreurs ? Par exemple si je me trompe de type pour $name ou si j’oublie tout simplement de le spécifier ?

Exemple un peu plus compliqué

J’utilise la librairie vlucas/phpdotenv, et j’ai écris le code suivant :

  /**
   * Load environment variables from the `.env` file at the root of the project.
   */
  public static function loadEnv(): void
  {
    $dotenv = \Dotenv\Dotenv::createImmutable(__DIR__ . '/..');

    $dotenv->load();
    $dotenv->required(['BASE_DIR', 'APP_NAME', 'LOG_PATH', 'ENV'])->notEmpty();
  }

Ici, je ne fais qu’utiliser des méthodes qui ont déjà été testées avec vlucas/phpdotenv. Y a t-il un intérêt à tester celle-ci ?

Exemple encore plus compliqué

J’utilise ici la librairie filp/whoops, et j’ai écris le code suivant :

  /**
   * Display an error page for every exception.
   */
  public static function runWhoops(): void
  {
    $whoops = new \Whoops\Run();

    if (self::getEnv('ENV') !== 'prod') {
        $whoops->pushHandler(new \Whoops\Handler\PrettyPageHandler);
    } else {
        $whoops->pushHandler(function($e) {
            self::say(
              '<strong style="color: red;">This is production, so this is a friendly error page !</strong>'
            );
        });
    }

    $whoops->register();
  }

Le if dépend d’une variable d’environnement qui peut seulement être modifié directement au sein du fichier .env à la racine du projet. Ainsi, il est délicat de tester cette condition.

Au final je ne sais pas trop quoi et comment tester et j’aurais besoin de vos conseils.

Merci.

+0 -0

Hello

Tout dépend de ce qui est critique pour ton usage : est-ce que tu as besoin de gérer les différentes erreurs derrière, ou tu considères que tu as une validation ailleurs qui t’assure que les noms sont OK par exemple ?


Pour ton deuxième exemple je vois peu d’intérêt de tester du code si simple, mais ce qui peut être intéressant est surtout de valider ce qui a été appelé et avec quels arguments, pas ce que les fonctions en question font (puisque, comme tu le dis, c’est déjà testé en amont).


Dans ton troisième exemple, en tout logique tu dois pouvoir "mocker" des fonctions, et donc forcer des valeurs de test te permettant de contrôler ton exécution pour valider le comportement de ta fonction.

Tu peux donc simuler que getEnv renvoie 'prod' et valider que ça passe bien dans le if (en regardant si $whoops->pushHandler() est bien appelé avec new \Whoops\Handler\PrettyPageHandler en paramètre).

Merci pour ta réponse.

Concernant le premier exemple, il semble que PHP prenne automatiquement en compte le manque d’argument ou un mauvais typage (avec declare(strict_types=1)) donc il ne me semble pas nécessaire de préciser.

Pour le troisième exemple, j’ai un peu modifié la méthode pour faciliter les tests :

  /**
   * Display an error page for every exception.
   * 
   * @param \Whoops\RunInterface $whoops \Whoops\Run instance
   * @param bool $isProd                 Whether the app is in production.
   */
  public static function runWhoops(\Whoops\RunInterface $whoops, bool $isProd): void
  {
    if (!$isProd) {
      $whoops->pushHandler(new \Whoops\Handler\PrettyPageHandler);
    } else {
      $whoops->pushHandler(function ($e) {
        echo '<h1 style="color: red;">This is production, so this is a friendly error page !</h1>';
      });
    }

    $whoops->register();
  }

  App::runWhoops( // on appelle la méthode
    new \Whoops\Run(),
    filter_var(Env::get('is_prod'), FILTER_VALIDATE_BOOLEAN)
  );

Du coup j’ai pense à quelque chose comme ça :

  protected $whoops;

  public function testRunWhoopsLocal()
  {
    $this->whoops = $this->createMock(\Whoops\RunInterface::class);

    $this->whoops->expects($this->once())
        ->method('pushHandler')
        ->with(new \Whoops\Handler\PrettyPageHandler);

    App::runWhoops($this->whoops, false);
  }

  public function testRunWhoopsProd()
  {
    $this->whoops = $this->createMock(\Whoops\RunInterface::class);

    $this->whoops->expects($this->once())
        ->method('pushHandler')
        ->with(
            function ($e) {
              echo 'This is production, so this is a friendly error page !';
            }
          );

    App::runWhoops($this->whoops, true);
  }

Je ne sais pas si c’est optimal mais les tests fonctionnent. En revanche, concernant le second exemple, PHPUnit ne peut pas mock des méthodes statiques (ici \Dotenv\Dotenv::createImmutable(__DIR__ . '/..')).

+0 -0
Connectez-vous pour pouvoir poster un message.
Connexion

Pas encore membre ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte