IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Langage C++ Discussion :

Test unitaire et membre protégé


Sujet :

Langage C++

  1. #1
    Invité
    Invité(e)
    Par défaut Test unitaire et membre protégé
    Bonjour,

    j'ai actuellement une question assez bête, mais je suis pas assez rodé sur le langage pour trouver moi même la réponse...

    J'ai une certaine classe, mettons NeuronFactory, qui me retourne des Neuron*.
    Now, j'aimerais tester une certaine propriété (la propriété value bien mise à 10 de mon neurone de type Neuron*), mais qui est protégée.

    Du coup, j'ai qqch dans le genre :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
     
    class NeuronFactory{
      Neuron *getNeuron(int value){return new Neuron(value);}
    };
     
    class Neuron{
      public:
        Neuron(int a):value(a){}
      protected:
        int value;
    };
     
    class NeuronTest:public Neuron{
      //using Neuron::value; n'y change rien
      NeuronTest(Neuron* n):Neuron(n->value){} //Erreur ici, value is protected
      int getValue(){return value;}
    };
    void test(){
      //un test qui vérifie que la value est bien settée par le neurone
      NeuronFactory n;
      Neuron* neuron = n->getNeuron(10);
      NeuronTest* neuronTest = new NeuronTest(neuron);
      Tester.is(neuronTest->getValue(), 10);
    }
    Mon idée, c'est que lorsqu'on accède depuis une classe à un objet de la même classe, alors les valeurs sont "publiques", typiquement NeuronTest(NeuronTest b) permet d'accéder aux propriétés de b. Mais ici, NeuronTest bien que fille de Neuron ne permet pas d'accéder aux propriétés de Neuron. Même en précisant le using Neuron::value;

    Avez vous une idée?

  2. #2
    Membre Expert

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2007
    Messages
    1 895
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Septembre 2007
    Messages : 1 895
    Par défaut
    Citation Envoyé par galerien69 Voir le message
    Mon idée, c'est que lorsqu'on accède depuis une classe à un objet de la même classe, alors les valeurs sont "publiques", typiquement NeuronTest(NeuronTest b) permet d'accéder aux propriétés de b. Mais ici, NeuronTest bien que fille de Neuron ne permet pas d'accéder aux propriétés de Neuron. Même en précisant le using Neuron::value;

    Avez vous une idée?
    Pourquoi est-ce qu'une classe de test aurait besoin de connaitre l'invariant d'une autre classe ? Le test est là pour tester la validité des pré-conditions / post-conditions, ainsi que la validité du résultat.

    SI tu passe outre cette remarque:
    * Ta classe de test n'hérite pas de ta classe. C'est une très mauvaise chose.
    * Si tu souhaite vraiment avoir un accès à l'invariant de ta classe, alors ta classe de test est une classe amie de ta classe testée.

    Je préviens quand même que cette dernière solution, bien que répondant à tes attentes, n'est PAS une bonne solution. La classe doit être designée de manière à toujours dissimuler son invariant, et la classe de test a pour but de tester une interface. Tester les détails d'implémentation n'a pas de sens.
    [FAQ des forums][FAQ Développement 2D, 3D et Jeux][Si vous ne savez pas ou vous en êtes...]
    Essayez d'écrire clairement (c'est à dire avec des mots français complets). SMS est votre ennemi.
    Evitez les arguments inutiles - DirectMachin vs. OpenTruc ou G++ vs. Café. C'est dépassé tout ça.
    Et si vous êtes sages, vous aurez peut être vous aussi la chance de passer à la télé. Ou pas.

    Ce site contient un forum d'entraide gratuit. Il ne s'use que si l'on ne s'en sert pas.

  3. #3
    gl
    gl est déconnecté
    Rédacteur

    Homme Profil pro
    Inscrit en
    Juin 2002
    Messages
    2 165
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 46
    Localisation : France, Isère (Rhône Alpes)

    Informations forums :
    Inscription : Juin 2002
    Messages : 2 165
    Par défaut
    Citation Envoyé par Emmanuel Deloget Voir le message
    Pourquoi est-ce qu'une classe de test aurait besoin de connaitre l'invariant d'une autre classe ? Le test est là pour tester la validité des pré-conditions / post-conditions, ainsi que la validité du résultat.
    Peut-être pour tester que justement l'invariant de classe est bien respecté au sein de la classe. Non ?
    Cela ne me semble pas totalement illogique que les tests unitaires valides également les invariants..

  4. #4
    Inactif  


    Homme Profil pro
    Inscrit en
    Novembre 2008
    Messages
    5 288
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Secteur : Santé

    Informations forums :
    Inscription : Novembre 2008
    Messages : 5 288
    Par défaut
    Peut-être pour tester que justement l'invariant de classe est bien respecté au sein de la classe. Non ?
    Cela ne me semble pas totalement illogique que les tests unitaires valides également les invariants.
    Si l'invariant est public, ça aurait un sens de le tester (par exemple un numéro d'identification qui serait attribué automatiquement et que l'on pourrait lire mais pas modifier)
    Ici, c'est un invariant que l'on ne peut ni lire, ni modifier (donc un détail d'implémentation, servant en interne). Il faut plutôt tester que les fonctions qui utilisent cet invariant respectent bien leurs contrats, peux importe la gestion interne, non ?

  5. #5
    Membre Expert

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2007
    Messages
    1 895
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Septembre 2007
    Messages : 1 895
    Par défaut
    Citation Envoyé par gl Voir le message
    Peut-être pour tester que justement l'invariant de classe est bien respecté au sein de la classe. Non ?
    Cela ne me semble pas totalement illogique que les tests unitaires valides également les invariants..
    L'invariant de la classe est supposé être respecté à partir du moment ou là classe effectue le travail qu'elle doit faire. Le cas ou la classe fonctionne exactement comme prévu mais que l'invariant de la classe peut être dans un stade où il n'est plus valide est impossible (démonstration par l'absurde : si une classe avec des données intrinsèque respecte en permanence sont contrat mais que son invariant est invalide, ça veut dire que les données intrinsèques sont à la fois valides et invalides),

    L'invariant est réellement un détail d'implémentation. Tester l'invariant signifie que l'on doit connaitre de détails d'implémentation. Un changement peut alors provoquer la nécessiter de réécrire les tests, sans pour autant que la valeur des tests ne changent - puisque ce qu'on leur demande en principal, c'est de vérifier les fonctionnalités offertes par une classe.
    [FAQ des forums][FAQ Développement 2D, 3D et Jeux][Si vous ne savez pas ou vous en êtes...]
    Essayez d'écrire clairement (c'est à dire avec des mots français complets). SMS est votre ennemi.
    Evitez les arguments inutiles - DirectMachin vs. OpenTruc ou G++ vs. Café. C'est dépassé tout ça.
    Et si vous êtes sages, vous aurez peut être vous aussi la chance de passer à la télé. Ou pas.

    Ce site contient un forum d'entraide gratuit. Il ne s'use que si l'on ne s'en sert pas.

  6. #6
    gl
    gl est déconnecté
    Rédacteur

    Homme Profil pro
    Inscrit en
    Juin 2002
    Messages
    2 165
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 46
    Localisation : France, Isère (Rhône Alpes)

    Informations forums :
    Inscription : Juin 2002
    Messages : 2 165
    Par défaut
    Citation Envoyé par gbdivers Voir le message
    Ici, c'est un invariant que l'on ne peut ni lire, ni modifier (donc un détail d'implémentation, servant en interne). Il faut plutôt tester que les fonctions qui utilisent cet invariant respectent bien leurs contrats, peux importe la gestion interne, non ?
    De mon point de vue, lors du test unitaire de fonctions membres de la classe (ou de fonctions non-membres amies de cette classe), tester que l'invariant est toujours vérifié après l'appel de la fonction me semble être plutôt une bonne idée.
    Maintenant, un invariant de classe qui n'est pas visible publiquement (directement ou indirectement via d'autres éléments) ça ne court pas non plus les rues. Assez fréquemment, la validation des post-conditions permet de valider également les invariants de classes.

  7. #7
    gl
    gl est déconnecté
    Rédacteur

    Homme Profil pro
    Inscrit en
    Juin 2002
    Messages
    2 165
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 46
    Localisation : France, Isère (Rhône Alpes)

    Informations forums :
    Inscription : Juin 2002
    Messages : 2 165
    Par défaut
    Citation Envoyé par Emmanuel Deloget Voir le message
    L'invariant de la classe est supposé être respecté à partir du moment ou là classe effectue le travail qu'elle doit faire. Le cas ou la classe fonctionne exactement comme prévu mais que l'invariant de la classe peut être dans un stade où il n'est plus valide est impossible (démonstration par l'absurde : si une classe avec des données intrinsèque respecte en permanence sont contrat mais que son invariant est invalide, ça veut dire que les données intrinsèques sont à la fois valides et invalides),

    L'invariant est réellement un détail d'implémentation. Tester l'invariant signifie que l'on doit connaitre de détails d'implémentation. Un changement peut alors provoquer la nécessiter de réécrire les tests, sans pour autant que la valeur des tests ne changent - puisque ce qu'on leur demande en principal, c'est de vérifier les fonctionnalités offertes par une classe.
    OK, je comprends. Globalement je suis d'accord avec toi.

    De mon point de vue, ici tu testes bel et bien le respect de l'invariant. Si ce n'est que tu ne vas pas le faire en inspectant "l'intérieur" de la classe mais en inspectant ses conséquences sur l'état publique de la classe.

    En fait, je pense que j'ai mal compris ta première intervention. Visiblement tu parlais de l'invariant en terme d'implémentation. Alors que lorsque je parle d'invariant de classe, je parle d'un point de vue de la logique de la classe.

  8. #8
    Invité
    Invité(e)
    Par défaut
    Merci pour les réponses déjà
    Par contre, je vous suis pas trop...je reprends.
    Pourquoi est-ce qu'une classe de test aurait besoin de connaitre l'invariant d'une autre classe
    Ici, mon but, c'est de tester que la factory me retourne bien un neurone correspondant aux paramètres passés à la méthode appelée.
    Je récupère en sortie un neurone. Son "identifiant" sont les parametres passés au constructeur. Je sais qu'ils sont bien settés (test sur le constructeur). Je veux m'assurer que la factory crée correctement mon neurone en testant son identifiant. Donc je teste pa l'invariant de mon neurone "en soi", je récupère sa valeur pour affirmer que la factory me retourne bien le bon neurone!

    Ma classe de test est juste une solution (inspirée php et phpunit) ou l'on dérive une classe pour pouvoir accéder aux attributs privés/protected et surcharger les méthodes etc.

    La classe doit être designée de manière à toujours dissimuler son invariant, et la classe de test a pour but de tester une interface
    Je ne vois pas ce que tu entends par classe de test.
    Pour moi une classe de test, c'est juste une classe qui fournit des methodes style Test::is(obj->doSomething(), resultatAttendu);
    Pe veux tu dire par la que l'ensemble des tests réalisés sur une classe sont faits par une classe de tests?

    Visiblement tu parlais de l'invariant en terme d'implémentation. Alors que lorsque je parle d'invariant de classe,
    Ici, c'est ce qui m'empêche de profiter de vos posts, je ne comprends pas ce qu'on entend par invariant.
    Qu'est-ce qu'un invariant en terme d'implémentation?
    Qu'est-ce qu'un invariant de classe?
    d'apres wiki, un invariant de classe peut être une condition implicite sur un entier. Ex : 1<entier<10.
    A partir de là, ca me parait normal de tester que si en entrée d'une fonction j'ai un param correct, que l'entier est bien modifié avec des valeurs correctes? OK, l'utilisateur il s'en tape un peu de comment ca se passe dans la classe, mais par exemple si derrière jutilise une autre méthode qui s'appuie justement sur un attribut qui est des lors invalide, ben la fonction elle même fait bien son stuff, mais le résultat final est incohérent!

    Bref, quelqu'un pour reprendre mollo avec moi?

    J'avais aussi envie de revenir sur la démo par l'absurde, mais déjà si on peut déblayer cette notion d'invariant...

    Pour revenir au problème initial. Si j'ai un setter, j'ai quand même envie de tester que la valeur que je sette est correctement settée. Non? Ou c'est justement ce quil ne faut pas faire ?

  9. #9
    Invité
    Invité(e)
    Par défaut
    je ré-up ce post.

    Le problème soulevé m'intéresse, et je reviens à la charge!
    Donc d'abord, après avoir lu sur le net : un invariant de classe est une condition validant l'étant de la classe qui n'est jamais modifiée.

    Bon, on peut étendre à chaque appel d'une méthode publique, je présume.

    Après, c'était pas ce que je teste, mais c'est un détail.

    J'aimerais qu'on m'explique un peu plus la différence entre le détail d'implem et la logique de classe.
    A supposer qu'une méthode publique manipule cet invariant au sein de son corps, n'est-il pas légitime de vouloir tester qu'en sortie celui-ci a bien été laissé inchangé? Et là, je ne vois qu'un test sur de l'implem.

  10. #10
    Membre Expert

    Profil pro
    Inscrit en
    Juin 2006
    Messages
    1 294
    Détails du profil
    Informations personnelles :
    Localisation : Royaume-Uni

    Informations forums :
    Inscription : Juin 2006
    Messages : 1 294
    Par défaut
    Salut,

    Le principe des tests (unitaires) est d'exercer l'interface publique d'un composant à un niveau suffisamment rassurant tout en gênant le moins possible des remaniements ultérieurs.
    En général si après avoir écrit tous les tests possibles qui se limitent à l'interface publique tu estimes que tu n'es pas encore assez rassuré, c'est un bon signe que peut-être le composant est trop gros et devrait être découpé.

    J'ajouterais à ça que les tests sont très liés au design des composants, donc les mêmes règles s'appliquent, entre autre qu'il vaut mieux penser en terme de traitements à effectuer plutôt qu'en terme de données à manipuler.
    Le 'push' ("tiens fais-moi donc ça avec les données que voici") est souvent préférable au 'pull' ("file moi ça, ça et ça comme données que je puisse faire mon traitement").
    Ce qui conduit à l'utilisation de mock objects.

    Le code de test gagne à être exploité en tant que premier code écrit (idéalement avant le code de production même, cf. TDD) qui manipule un composant.
    L'écrire comme du code utilisateur "normal" permet d'avoir des retours immédiats sur l'interface publique du composant, et de faire les ajustements nécessaires dès le début.

    Donc en résumé, à mon avis, non seulement il n'y a pas besoin de tester l'implémentation (au mieux c'est un code smell) mais il est même bénéfique de ne pas le faire !

    MAT.

  11. #11
    Invité
    Invité(e)
    Par défaut
    Hello,

    merci pour cette réponse, j'y vois plus clair.
    Jvais me documenter du coté des mocks.

    :-)

  12. #12
    Membre Expert

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2007
    Messages
    1 895
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Septembre 2007
    Messages : 1 895
    Par défaut
    Citation Envoyé par galerien69 Voir le message
    A supposer qu'une méthode publique manipule cet invariant au sein de son corps, n'est-il pas légitime de vouloir tester qu'en sortie celui-ci a bien été laissé inchangé? Et là, je ne vois qu'un test sur de l'implem.
    Attention sur la notion d'invariant : il est bien question de quantités qui peuvent être modifiées, mais qui, prisent ensemble, vérifient toujours une condition qui est constante.

    Certaines de ces conditions sont très simples (par exemple, ptr_intelligent.m_ptr != NULL). Certaines sont plus complexes (chaine.m_ptr == NULL || (chaine.m_ptr != NULL && chaine.m_length == strlen(chaine.m_ptr)) ; on peut bien sûr imaginer n'importe quel degré de complexité.

    Ces conditions sont autant de relations permettant de définir qu'une classe est dans un état valide ou non. Elles peuvent être implémentées sous la forme de post-conditions dans les méthodes de classe, si tu as besoin de les définir de manière explicite. Mais le fait est qu'elle sont dépendantes de l'implémentation de la classe - et pas de la fonction offerte par la classe, qui peut être implémenté de différente manière. Du coup, l'invariant n'est pas nécessairement (en fait, très rarement même) visible au niveau de l'interface de la classe, et c'est une bonne chose.

    Si tu souhaites tester l'invariant de la classe, je te suggère d'ajouter une simple fonction publique qui vérifie l'ensemble des conditions permettant de prouver que l'instance est dans un état valide. Cette fonction, qui renverrai un booléen, te permettra de vérifier dans ta classe de test l'état de l'invariant, sans pour autant avoir à le rendre public. J'attire quand même ton attention sur le fait que même ça, c'est une solution bâtarde : si ton interface se comporte correctement pour tous les cas d'utilisation, ça signifie que ton invariant n'est violé à aucun moment. Dans le cas contraire - == l'état interne de l'instance est incohérent - tu obtiendrais nécessairement un problème visible de l'interface à un moment ou à un autre.

    Je te donne un exemple : supposons que tu code un interpréteur simple, qui est capable d'exécuter des opérations mathématiques.

    1. Construction : interpretor(string chaine_operation) (forme de la chaine : x op y
    2. Initialisation: bind(non_variable, valeur)
    3. Execution: process() -> résultat.

    En interne, lors de la construction, tu crée un objet operation dépendant de l'opération demandée (op_addition, op_mult, ...) ainsi que des objets conteneurs pour les variables qui sont utilisées dans la chaine opération.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
    void test()
    {
       interpretor op("x + 2");
     
       SHALL_RAISE_EXCEPTION(op.process(), except::missing_binded_value);
       SHALL_NOT_RAISE_EXCEPTION(op.bind("x", 8));
       SHALL_NOT_RAISE_EXCEPTION(op.process());
       SHALL_BE_EQUAL(op.process(), result(10));
    }
    A aucun moment je n'ai besoin de tester ce qui est intérieur à la classe pour vérifier que son comportement est correct. Si l'objet interne op_xxx instancié n'est pas le bon, alors le dernier test va échouer. Si je n'ai pas instancier d'objet conteneur pour la variable x, alors le fait de demander le processing de l'opération sur le premier test ne va pas me dire que j'ai oublié de binder une valeur. Si je peux binder une valeur sur x, alors c'est que cet objet conteneur existe. Si ensuite je peux effectuer le calcul, c'est que j'ai une chaine de calcul entièrement valide et initialisée. A aucun moment je ne teste l'invariant de manière explicite, et pourtant, je l'ai probablement testé complètement.

    En interne, la classe peut être affreusement complexe. Mais au final, je m'en moque royalement, puisque ce que je souhaite tester, c'est mon interface : c'est à elle de se comporter correctement.
    [FAQ des forums][FAQ Développement 2D, 3D et Jeux][Si vous ne savez pas ou vous en êtes...]
    Essayez d'écrire clairement (c'est à dire avec des mots français complets). SMS est votre ennemi.
    Evitez les arguments inutiles - DirectMachin vs. OpenTruc ou G++ vs. Café. C'est dépassé tout ça.
    Et si vous êtes sages, vous aurez peut être vous aussi la chance de passer à la télé. Ou pas.

    Ce site contient un forum d'entraide gratuit. Il ne s'use que si l'on ne s'en sert pas.

  13. #13
    Membre Expert

    Profil pro
    Inscrit en
    Juin 2006
    Messages
    1 294
    Détails du profil
    Informations personnelles :
    Localisation : Royaume-Uni

    Informations forums :
    Inscription : Juin 2006
    Messages : 1 294
    Par défaut
    Citation Envoyé par galerien69 Voir le message
    Jvais me documenter du coté des mocks.
    Tu peux jeter un œil à cette liste des bibliothèques de mock objects pour C++.
    J'ai une nette préférence pour le dernier (Turtle), mais en même temps comme c'est moi qui l'ai fait je ne dois pas être très impartial...

    MAT.

Discussions similaires

  1. Tests unitaires & base de données
    Par lalystar dans le forum Test
    Réponses: 15
    Dernier message: 18/06/2010, 16h50
  2. Tests Unitaires - Production de documents
    Par giviz dans le forum Test
    Réponses: 13
    Dernier message: 07/02/2005, 08h41
  3. Tests unitaires en C#
    Par Bouboubou dans le forum Test
    Réponses: 2
    Dernier message: 01/10/2004, 13h03
  4. [TESTS] Tests unitaires
    Par mathieu dans le forum Test
    Réponses: 4
    Dernier message: 08/01/2004, 12h59

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo