Question

Donc, je lisais le blog de tests de Google, et il dit que l'état global est mauvais et rend difficile l'écriture de tests.Je le crois : mon code est difficile à tester pour le moment.Alors, comment puis-je éviter l’état global ?

La principale raison pour laquelle j'utilise l'état global (si je comprends bien) est la gestion des informations clés entre nos environnements de développement, d'acceptation et de production.Par exemple, j'ai une classe statique nommée "Globals" avec un membre statique appelé "dbConnectionString". Lorsque l'application se charge, il détermine la chaîne de connexion à charger et remplit Globals.DBConnectionString.Je charge les chemins de fichiers, les noms de serveurs et d'autres informations dans la classe Globals.

Certaines de mes fonctions reposent sur des variables globales.Ainsi, lorsque je teste mes fonctions, je dois me rappeler de définir d'abord certains paramètres globaux, sinon les tests échoueront.J'aimerais éviter cela.

Existe-t-il un bon moyen de gérer les informations d’état ?(Ou est-ce que je comprends mal l'état global ?)

Était-ce utile?

La solution

L'injection de dépendances est ce que vous recherchez.Plutôt que de laisser ces fonctions rechercher leurs dépendances, injectez les dépendances dans les fonctions.Autrement dit, lorsque vous appelez les fonctions, transmettez-leur les données qu'elles souhaitent.De cette façon, il est facile de mettre en place un cadre de test autour d'une classe, car vous pouvez simplement injecter des objets fictifs le cas échéant.

Il est difficile d'éviter un état global, mais la meilleure façon d'y parvenir est d'utiliser des classes d'usine au niveau le plus élevé de votre application, et tout ce qui se trouve en dessous de ce niveau le plus élevé est basé sur l'injection de dépendances.

Deux avantages principaux :premièrement, les tests sont beaucoup plus faciles, et deuxièmement, votre application est beaucoup plus faiblement couplée.Vous comptez sur la capacité de programmer sur l'interface d'une classe plutôt que sur son implémentation.

Autres conseils

Gardez à l'esprit que si vos tests impliquent des ressources réelles telles que des bases de données ou des systèmes de fichiers, alors ce que vous faites est tests d'intégration plutôt que tests unitaires.Les tests d'intégration nécessitent une configuration préliminaire tandis que les tests unitaires doivent pouvoir s'exécuter indépendamment.

Vous pouvez envisager d'utiliser un framework d'injection de dépendances tel que Castle Windsor, mais pour des cas simples, vous pourrez peut-être adopter une approche intermédiaire telle que :

public interface ISettingsProvider
{
    string ConnectionString { get; }
}

public class TestSettings : ISettingsProvider
{        
    public string ConnectionString { get { return "testdatabase"; } };
}

public class DataStuff
{
    private ISettingsProvider settings;

    public DataStuff(ISettingsProvider settings)
    {
        this.settings = settings;
    }

    public void DoSomething()
    {
        // use settings.ConnectionString
    }
}

En réalité, vous liriez probablement les fichiers de configuration de votre implémentation.Si vous êtes partant, un framework DI complet avec des configurations échangeables est la voie à suivre, mais je pense que c'est au moins mieux que d'utiliser Globals.ConnectionString.

Excellente première question.

La réponse courte :assurez-vous que votre application est une fonction de TOUTES ses entrées (y compris les implicites) jusqu'à ses sorties.

Le problème que vous décrivez ne semble pas être un état global.Du moins, pas d'état mutable.Au contraire, ce que vous décrivez ressemble à ce que l'on appelle souvent "le problème de configuration", et il propose un certain nombre de solutions.Si vous utilisez Java, vous souhaiterez peut-être vous pencher sur des frameworks d'injection légers tels que Guide.En Scala, cela est généralement résolu avec implicites.Dans certaines langues, vous pourrez charger un autre programme pour configurer votre programme au moment de l'exécution.C'est ainsi que nous configurions les serveurs écrits en Smalltalk, et j'utilise un gestionnaire de fenêtres écrit en Haskell appelé Xmonad dont le fichier de configuration n'est qu'un autre programme Haskell.

Un exemple d'injection de dépendances dans un paramètre MVC, voici :

index.php

$container = new Container();
include_file('container.php');

conteneur.php

container.add("database.driver", "mysql");
container.add("database.name","app");

...

$container.add(new Database($container->get('database.driver', "database.name")), 'database');
$container.add(new Dao($container->get('database')), 'dao');
$container.add(new Service($container->get('dao')));
$container.add(new Controller($container->get('service')), 'controller');

$container.add(new FrontController(),'frontController');

index.php continue ici :

$frontController = $container->get('frontController');
$controllerClass = $frontController->getController($_SERVER['request_uri']);
$controllerAction = $frontController->getAction($_SERVER['request_uri']);
$controller = $container->get('controller');
$controller->$action();

Et là, vous l'avez, le contrôleur dépend d'un objet de couche de service qui dépend d'un objet DAO (objet d'accès aux données) qui dépend d'un objet de base de données avec le pilote de base de données, le nom, etc.

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