Classes de tests unitaires dotées de fonctionnalités en ligne

23

Lors du test unitaire des fonctions d'une classe qui a des fonctions privées qui nécessitent des fonctionnalités en ligne. Comment procéder pour le tester?

Par exemple:

public class Foo
{
    public int methodA()
    {
         int val = goOnlineToGetVal();

         return val;
    }

    private int goOnlineToGetVal()
    {
        CloudService c = new CloudService();
        int oval = c.getValueFromService();
        return oval;
    }
}

Si je devais tester la fonction: 'methodA ()', il tenterait d'utiliser 'goOnlineToGetVal ()' qui, à son tour, essaierait de se connecter en ligne, si ce test était effectué sans fonctionnalité. Comment pourrais-je obtenir une couverture de classe à 100% sans aller en ligne?

Ben Crazed Up Euden
la source
Votre méthode appelle-t-elle simplement l'API cloud ou fait-elle également un travail supplémentaire?
Winston Ewert du
4
Injection de dépendance?
PhaDaPhunk

Réponses:

76

new CloudService()

Et il y a votre problème.

La conception OO moderne recommande que ce type de dépendance soit transmis plutôt que construit directement. Cela peut être transmis à la fonction elle-même ou à la classe au moment de la construction. Il peut également être saisi ou agrégé par un conteneur d'inversion de contrôle si ce type de complexité est justifié.

À ce stade, il devient assez banal de passer un service simulé / faux pour vous fournir vos données "en ligne" pendant les tests. Mieux encore, il permet à votre logiciel d'être suffisamment flexible, de sorte que vous puissiez vous adapter rapidement si un client (gouvernemental?) Arrive et ne veut pas utiliser le cloud pour ses valeurs. Ou vous voulez vider un fournisseur de cloud pour un autre. Ou...

Telastyn
la source
7
Je pense que je me suis beaucoup plus rapprochée de la compréhension de l'injection de dépendance.
marczellm
Où est le meilleur endroit pour créer toutes les instances dont mon application a besoin? Le plus près possible du sommet de ma candidature? Jusqu'à présent, vous ne pouvez appliquer l'injection de dépendance; à un moment donné, vous devrez créer les instances vous-même au lieu de les faire passer comme arguments.
Paul
3
@Paul - Cela dépend. D'après mon expérience, les applications ressemblent très souvent à des arbres binaires - une classe / fonction / module en colle deux pour fournir un comportement plus complexe. L'une de ces classes "glue" est celle qui spécifie l'implémentation concrète, qui à son tour est utilisée par quelque chose au-dessus qui la colle avec autre chose, qui à son tour ... jusqu'à ce que vous arriviez finalement à votre point d'entrée qui colle les gros morceaux ensemble de manière cohérente. Le «meilleur» endroit est celui qui permet à votre code de faire ce qu'il doit faire, sans le faire faire ni savoir ce qu'il ne devrait pas faire. Cela variera.
Telastyn
2
@Paul Habituellement, votre application est divisée entre une "bibliothèque", qui est directement testée et devrait utiliser ce type d'injection de dépendance, et du code spécifique à l'application. Ce dernier se résume généralement à une interface graphique, un service Web ou une interface de ligne de commande qui appelle directement la bibliothèque avec des données fournies par l'utilisateur. Essentiellement, le code spécifique à l'application créera les implémentations concrètes attendues par la bibliothèque à injection de dépendances.
Darkhogg
@Paul Jetez un œil aux conteneurs IoC comme Castle Windsor, StructureMap, Ninject et Unity. Ils ne sont en aucun cas le seul moyen de faire l'injection de dépendance, mais ils peuvent être très instructifs en termes de réflexion sur la construction d'un graphe d'objet, de haut en bas
Ben Aaronson
37

Je voudrais l'implémenter comme ceci:

public class Foo
{
    private ICloudService cloudService;

    public Foo(ICloudService s)
    {
       cloudService=s;
    }

    public int methodA()
    {
         int val = goOnlineToGetVal();

         return val;
    }

    private int goOnlineToGetVal()
    {
        int oval = cloudService.getValueFromService();
        return oval;
    }
}

L'interface ICloudServicepeut être implémentée soit avec une maquette pour les tests, soit avec le "vrai" cloudservice. Lorsque l'instanciation new CloudService()est obligatoire pour chaque appel de getValueFromService, ou CloudServiceprovient d'une API tierce qui ne peut pas être modifiée, implémentez un wrapper, dérivant ICloudServiceet effectuant les appels appropriés.

Doc Brown
la source
1
C'est définitivement la voie à suivre. Il existe des bibliothèques de classes qui peuvent simuler une interface vous donnant un contrôle absolu du test.
Greg Burghardt