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

Création de jeux vidéo Discussion :

L'usage d'assert dans tests unitaires pour les jeux vidéos


Sujet :

Création de jeux vidéo

  1. #1
    Inactif  


    Homme Profil pro
    Doctorant sécurité informatique — Diplômé master Droit/Économie/Gestion
    Inscrit en
    Décembre 2011
    Messages
    9 012
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 31
    Localisation : France, Loire (Rhône Alpes)

    Informations professionnelles :
    Activité : Doctorant sécurité informatique — Diplômé master Droit/Économie/Gestion
    Secteur : Enseignement

    Informations forums :
    Inscription : Décembre 2011
    Messages : 9 012
    Points : 23 211
    Points
    23 211
    Par défaut L'usage d'assert dans tests unitaires pour les jeux vidéos
    Citation Envoyé par yahiko Voir le message
    Tu devrais penser à utiliser à la macro assert() (<assert.h>) très utile et justement faite pour cet usage.
    Tous mes tests unitaires en C/C++ sont basés dessus.
    Si tu utilises assert(), ce n'est pas des tests unitaires.

    Si la condition est fausse, tu va planter.
    Tu ne vas donc pas pouvoir enchaîner les tests et tu ne sauras pas directement combien d'erreurs tu as.
    De plus, les assertions ne sont qu'une partie des tests unitaires, il y a tout ce qui est fixture, déclaration du test, informations relatives aux tests …
    Sans compter que tu ne pourras pas exploiter facilement les résultats.


    Si tu utilises les assert dans la méthode à tester :

    • pour tester les pré-condition


    Je pense qu'on touche un autre type de tests (tests d'intégration ?).

    Mais il faut aussi que ton code marche en release et donc qu'on puisse entrer des valeurs invalides sans qu'il y ai d'asserts activés.
    À moins bien sûr que tu n'indique que l'insertion de valeurs erronées doit entraîner un comportement indéterminé en release.

    Mais ce serait bien de pouvoir tester la présence de l'assert en debug. Mais là on touche la gestion des signaux…
    Acquitter un SIGABR devrait être facile, mais ce serait bien, quitte à faire, d'aussi d'acquitter d'autres signaux comme des SEGFAULT…


    • pour les post-conditions


    Si tu as des classes filles qui héritent d'une classe mère, elles ont normalement des mêmes post-conditions communes.
    Soit tu duplique ton code, soit tu utilises une méthode commune.
    On ne peut pas dire que ce soit vraiment génial. De plus des problèmes évoqués plus haut, on ne peut pas faire du TDD de cette manière.
    Le TDD c'est "on écrit un test, ça plante, on corrige en mode gros fainéant, ça ne plante plus, on cherche un nouveau test pour faire planter puis on itère".

    Le test des résultats doit donc être externalisé.
    Après, tester les post-conditions avec des asserts… c'est génial pour détecter des erreurs (tests intégration ?) mais :
    • il faut les écrire, je suis sûr qu'au bout d'un moment on a sa claque d'écrire toujours les mêmes choses ;
    • il faut éviter les redondances avec le code :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    int add(int a, int b)
    {  
        int result = a + b;
        assert( result == a + b);
        return result;
    }
    .
    Donc au final, je pense qu'il vaut mieux les utiliser pour vérifier les plages de valeurs en sorties, tout comme pour les pré-conditions avec les plages de valeurs en entrée.
    Le comportement, le résultat en lui-même sera plus du ressort des tests unitaires.


    Encore un autre problème, si tu testes ton code avec gdb, tu connais la ligne, le fichier, la valeur des variables, c'est génial.
    Mais personnellement, je ne lance que très rarement gdb quand je compile et que j'exécute.
    Si ça plante, je relance avec gdb.
    Mais quid du fait que tu distribues une version "debug" compilée à tes amis et qu'il y ai un bug qui se produit une fois tout les 32 du mois ?
    Comment fais-tu pour le reproduire ? Tu sais juste que ça a planté puis voilà.

    Mais là, on est plus du tout dans une problématique de tests unitaires.
    Pour cela, j'ai une autre astuce pour récupérer le signal et la pile d'appel (donc aussi fichier + ligne), même en release et ce gratuitement.
    Mais si tout a bien été testé unitairement, l'erreur ne devrait pas trop être éloignée.
    Ce serait bien d'y ajouter plus d'informations, comme la valeur de quelques variables utilisées, mais cela aurait un coût et il faut bien s'arrêter quelque part.

    En tout cas merci bien… à cause de toi, ça sent une version 0.0.2 avec gestion des signaux dans les tests unitaires

  2. #2
    Rédacteur/Modérateur

    Avatar de yahiko
    Homme Profil pro
    Développeur
    Inscrit en
    Juillet 2013
    Messages
    1 423
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Alpes Maritimes (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur

    Informations forums :
    Inscription : Juillet 2013
    Messages : 1 423
    Points : 8 699
    Points
    8 699
    Billets dans le blog
    43
    Par défaut L'usage d'assert dans tests unitaires pour les jeux vidéos
    Tout d'abord, il me semble que tu as une conception des tests unitaires beaucoup trop rigide.

    Les tests unitaires a d'abord pour finalité d'éliminer les bugs (en conception ou en régression).
    * Que l'on sache combien d'assertions (au sens général) passent ou échouent, c'est accessoire. Le principal étant de corriger les assertions ayant échoué. Et dans ce cas, autant commencer par la première. Si les assertions sont bien rédigées, dans un ordre de complexité croissante, cela ne pose aucun problème méthodologique et d'"exploitation" des résultats. Une erreur a été détectée. D'une façon ou d'une autre il va falloir la corriger.

    * Éliminer les bugs signifie aussi que la couverture fonctionnelle des tests unitaires doit être la plus large possible. Cela implique pisser du code et passer du temps à rédiger lesdits tests unitaires. Cette phase doit être la plus économique possible et ne doit pas tomber dans l'écueil de l'overenginering. Autant consacré son énergie sur le code livrable. Autrement dit, le but n'est pas de faire les tests les mieux écrits et la meilleure approche théorique, mais de faire le plus grand nombre de tests, couvrant un maximum de chemins. D'où la nécessité de rester simple dans cette approche.

    * Enfin, ce n'était pas mon propos, mais il existe un moyen simple pour désactiver les assert pour les tests en prérequis d'une fonction avec un simple define :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    #define assert(ignore)((void) 0)
    Tutoriels et FAQ TypeScript

  3. #3
    Inactif  


    Homme Profil pro
    Doctorant sécurité informatique — Diplômé master Droit/Économie/Gestion
    Inscrit en
    Décembre 2011
    Messages
    9 012
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 31
    Localisation : France, Loire (Rhône Alpes)

    Informations professionnelles :
    Activité : Doctorant sécurité informatique — Diplômé master Droit/Économie/Gestion
    Secteur : Enseignement

    Informations forums :
    Inscription : Décembre 2011
    Messages : 9 012
    Points : 23 211
    Points
    23 211
    Par défaut
    Pour être bien sûr, tes assertions, tu les fais dans tes méthodes ou dans tes tests ?


    Citation Envoyé par yahiko Voir le message
    Les tests unitaires a d'abord pour finalité d'éliminer les bugs (en conception ou en régression).
    Pour être exact, de vérifier/valider un comportement, c'est plus fort que d'éliminer des bugs, c'est aussi garantir une certaine qualité.

    * Que l'on sache combien d'assertions (au sens général) passent ou échouent, c'est accessoire. Le principal étant de corriger les assertions ayant échoué. Et dans ce cas, autant commencer par la première.
    Je ne suis pas d'accord, si tu as 200 assertions qui échouent, ce n'est pas pareil que d'en avoir qu'une seule qui échoue.
    De plus, ceci permet de savoir si tu progresses ou si tu casse encore plus de choses.

    Si les assertions sont bien rédigées, dans un ordre de complexité croissante, cela ne pose aucun problème méthodologique et d'"exploitation" des résultats. Une erreur a été détectée. D'une façon ou d'une autre il va falloir la corriger.
    Pas nécessairement.
    Tu peux modifier l'affichage des messages, pour par exemple pouvoir te rendre à la ligne incriminée en un clic fichier:ligne sous QtCreator il me semble.
    Voir l'interfacer avec un plugin quelconque.
    Tu peux aussi en profiter pour mesurer le temps d'exécution, cela ne coûte rien et permet de repérer des choses anormales.
    Tu peux aussi ajouter des messages plus explicites, gérer les exceptions.
    Tu peux aussi exporter les résultats pour les donner/montrer à un autre dev.
    Tu peux aussi t'amuser à faire des stats ou un affichage plus user-friendly avec des petites couleurs.
    Tu peux aussi avoir directement des valeurs de certaines variables, …

    * Éliminer les bugs signifie aussi que la couverture fonctionnelle des tests unitaires doit être la plus large possible. Cela implique pisser du code et passer du temps à rédiger lesdits tests unitaires. Cette phase doit être la plus économique possible et ne doit pas tomber dans l'écueil de l'overenginering.
    C'est pour cela que j'essaye de rendre mon "moteur"/"framework" ? de test le moins verbeux possible.

    Autant consacré son énergie sur le code livrable.
    Tu ne peux pas couvrir certains cas avec cette méthode et ce n'est pas 1 an plus tard, quand on tombera sur un cas particulier qu'il faudra se replonger dans ton code où on y comprendra plus rien qu'il faudra s'inquiéter. D'autant plus que si cela intervient dans des cas particuliers, bonjour pour le reproduire.
    Ici tu ne pourrais te concentrer que sur les plages de valeurs en entrée et en sortie. Tu ne pourras en aucun cas valider les comportements (si assertions dans le code et non dans les tests).

    De plus si tu as des classes filles, bonjour les copiés-collé et la perte de temps engendré.

    Autrement dit, le but n'est pas de faire les tests les mieux écrits et la meilleure approche théorique, mais de faire le plus grand nombre de tests, couvrant un maximum de chemins. D'où la nécessité de rester simple dans cette approche.
    Bien sûr, mais ceci n'empêche pas une approche par TDD.
    Il y a juste un juste milieu.

    Mais le temps perdu à écrire les tests, c'est du temps gagné sur le débogue plus tard et est un gage de qualité.


    * Enfin, ce n'était pas mon propos, mais il existe un moyen simple pour désactiver les assert pour les tests en prérequis d'une fonction avec un simple define :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    #define assert(ignore)((void) 0)
    Sauf que si on ignore l'assertion, que fera le reste du code ? Comportement indéterminé ? Segfault ? .

  4. #4
    Rédacteur/Modérateur

    Avatar de yahiko
    Homme Profil pro
    Développeur
    Inscrit en
    Juillet 2013
    Messages
    1 423
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Alpes Maritimes (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur

    Informations forums :
    Inscription : Juillet 2013
    Messages : 1 423
    Points : 8 699
    Points
    8 699
    Billets dans le blog
    43
    Par défaut
    J'ai lu ton pavé.

    Il vaut mieux pour toi que tu continues dans ta voie. L'expérience te montrera l'indispensable du superflu.

    Au plaisir.
    Tutoriels et FAQ TypeScript

  5. #5
    Inactif  


    Homme Profil pro
    Doctorant sécurité informatique — Diplômé master Droit/Économie/Gestion
    Inscrit en
    Décembre 2011
    Messages
    9 012
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 31
    Localisation : France, Loire (Rhône Alpes)

    Informations professionnelles :
    Activité : Doctorant sécurité informatique — Diplômé master Droit/Économie/Gestion
    Secteur : Enseignement

    Informations forums :
    Inscription : Décembre 2011
    Messages : 9 012
    Points : 23 211
    Points
    23 211
    Par défaut
    Citation Envoyé par yahiko Voir le message
    Il vaut mieux pour toi que tu continues dans ta voie. L'expérience te montrera l'indispensable du superflu.
    Que veux-tu dire par là ?
    • le superflu d'aujourd'hui est l'indispensable de demain ;
    • continu comme ça, plantes-toi et apprends de ton expérience ;
    • ?


    Il faut comprendre que le TDD est une technique de développement qui a ses règles.
    Alors oui, elle a des faiblesses mais aussi des forces.

  6. #6
    Membre éprouvé Avatar de maeiky
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Juin 2013
    Messages
    201
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Canada

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juin 2013
    Messages : 201
    Points : 976
    Points
    976
    Par défaut
    Je crois que ça veut dire : L'expérience te montrera la différence entre l'indispensable du superflu.

    Tout dépend de jusqu'où va ton idée et il est difficile d'en comprendre la porté.

    Si tu link par exemple SFML, les testes unitaire sur cette librairie sont probablement un peu inutile car elle a sans doute été déjà été très bien testé.

    Si ton idée va plus loin et que tu souhaite faire en sorte que tout les programmeurs travaillent en module et que l'on inclue les bibliothèques dynamiques de monsieur tout le monde, les testes unitaires deviennent peut-être indispensable.

    Quand tu dis ceci pour la distributions des binaires :

    Pour cela, j'ai plusieurs choix :

    créer un service de compilation en ligne ;
    récupérer les sources et les compiler moi même pour des versions officielles ;
    donner les spécifications et laisser les devs s'adapter pour être compatibles ;
    ajouter un cmake + g++ dans l'archive ;
    désigner des personnes pour faire office de distributeurs.
    Mis à part la solution du cmake, ça me semble impossible, à moins de disposer des serveurs de Google, ou bien d’avoir vraiment du temps à perde pour compiler leur code et d'avoir des clients très patient.
    (+ ou -) HS

    Je te suggère donc d'éditer le premier post de ton moteur et de spécifier clairement ton idée complète et tout ce que ça implique avant que l'on saute aux conclusions trop rapidement.
    Linx, un nouveau langage intuitif
    Simacode IDE, auto-complétion & compilation instantané
    GZE, moteur 2d/3d multi-langage/multi-plateforme

  7. #7
    Inactif  


    Homme Profil pro
    Doctorant sécurité informatique — Diplômé master Droit/Économie/Gestion
    Inscrit en
    Décembre 2011
    Messages
    9 012
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 31
    Localisation : France, Loire (Rhône Alpes)

    Informations professionnelles :
    Activité : Doctorant sécurité informatique — Diplômé master Droit/Économie/Gestion
    Secteur : Enseignement

    Informations forums :
    Inscription : Décembre 2011
    Messages : 9 012
    Points : 23 211
    Points
    23 211
    Par défaut
    Citation Envoyé par maeiky Voir le message
    Si tu link par exemple SFML, les testes unitaire sur cette librairie sont probablement un peu inutile car elle a sans doute été déjà été très bien testé.
    Oui, bien sûr, ceci est complètement inutile.

    Si ton idée va plus loin et que tu souhaite faire en sorte que tout les programmeurs travaillent en module et que l'on inclue les bibliothèques dynamiques de monsieur tout le monde, les testes unitaires deviennent peut-être indispensable.
    Oui, c'est cela.
    Le but, entre autres, est de fournir une interface (fichiers .h) et le comportement (tests unitaires) puis de laisser chacun faire ce qu'il souhaite et proposer ses propres modules. Cela facilite grandement le travail en équipe et me permet de vérifier que tout est correct.

    à moins de disposer des serveurs de Google
    En même temps si un jour j'ai besoin des serveurs de Google, c'est que mon projet sera bien plus qu'une réussite ^^.

    Je te suggère donc d'éditer le premier post de ton moteur et de spécifier clairement ton idée complète et tout ce que ça implique avant que l'on saute aux conclusions trop rapidement.
    J'ai commencé la documentation doxygen, mais c'est beaucoup de travail.
    Je changerais le sujet quand tout sera prêt.

  8. #8
    Membre éclairé

    Profil pro
    Inscrit en
    Décembre 2013
    Messages
    393
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2013
    Messages : 393
    Points : 684
    Points
    684
    Par défaut
    Les assert sont important, ils permettent de détecter une erreur de programmation très tôt. Mais cela ne remplace pas des tests unitaires.

    On va généralement travailler en debug, pour avoir un maximum d'infos lors d'un crash, pour corriger plus facilement (donc les assert sont intéressant dans ce cas). Mais on va livrer un programme compiler en release, donc sans les assert = il faut aussi tester le programme dans ce mode, ce que l'on peut faire qu'avec des tests, pas des assert.

    On va également vouloir tester les différentes plateformes que l'on utilise (win, linux, mac, android, ios, etc) avec différentes configurations matérielles (gpu nvidia/amd/intel, etc). Cela prend du temps (rien que pour compiler, cela peut prendre des heures), donc on va pas s'amuser a tout compiler et tester sur son poste de dev = les tests vont permettre d'automatiser cette tache. Mais pour cela, il ne faut pas que les tests s’arrêtent a la première erreur, mais que tous les tests soient lancés. Les assert ne conviennent pas non plus ici.

    Bref, créer des tests en utilisant les assert n'est pas très productif (ce qui n’enlève pas l’intérêt des assert, mais pour d'autres choses)

  9. #9
    Membre émérite
    Avatar de skeud
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Juin 2011
    Messages
    1 091
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 34
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juin 2011
    Messages : 1 091
    Points : 2 724
    Points
    2 724
    Billets dans le blog
    1
    Par défaut
    Pour compléter la discussion:

    Les assert doivent etre au niveau des tests, il ne doit pas y avoir d'assert dans un programme:
    _ un assert dans un programme équivaut à un exit(0); avec un message en plus.
    _ un assert sert à arréter le programme afin qu'il ne parte pas en vrille, il vaut mieux utiliser les exceptions dans ce cas ou un retour d'erreur.

    En revanche dans les tests unitaire, on les construit généralement de cette manière:
    Un programme lançant les test unitaire dans un processus séparé.
    Un programme par test unitaire (avec bien sur une lib d'initialisation des test commune à tout les test).
    Un flow d'execution simple, qui teste une seule fonctionnalité, et un assert à chque test de retour de fonction
    Des tests, des tests, et encore des tests.

    Le programme maitre va donc lancer chacun des test les uns après les autres, et recueillir les informations en fonctions des retour.

    Petit exemple:
    Pour une fonction qui va lire une chaine de caractère (genre strlen), il faudra faire les tests suivant:
    _ test de chaine normal
    _ test de chaine longue
    _ test de chaine vide
    _ test de chaine null
    _ test de chaine invalide (pointeur sur mémoire)

    On fait donc un seul .C qui testera tout ça. Puis un autre qui testera une autre méthode etc ......
    Ensuite cette "charte" n'est pas a prendre au pied de la lettre, il n'est pas rare d'avoir des test unitaire qui vont tester une fonctionnalité faisant appel à plusieurs méthode, et c'est bien pratique pour les tests automatique et de non regression .
    Pas de solution, pas de probleme

    Une réponse utile (ou +1) ->
    Une réponse inutile ou pas d'accord -> et expliquer pourquoi
    Une réponse à votre question


  10. #10
    Inactif  


    Homme Profil pro
    Doctorant sécurité informatique — Diplômé master Droit/Économie/Gestion
    Inscrit en
    Décembre 2011
    Messages
    9 012
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 31
    Localisation : France, Loire (Rhône Alpes)

    Informations professionnelles :
    Activité : Doctorant sécurité informatique — Diplômé master Droit/Économie/Gestion
    Secteur : Enseignement

    Informations forums :
    Inscription : Décembre 2011
    Messages : 9 012
    Points : 23 211
    Points
    23 211
    Par défaut
    Un programme par test unitaire (avec bien sur une lib d'initialisation des test commune à tout les test).
    Cela me semble un peu dur.

    Personnellement, je pense qu'il faut avoir du courage pour créer un fichier par petit test faire les fixtures, etc.
    Pourquoi ne pas regrouper des séries de tests unitaires dans le même fichier/programme ?


    Un programme lançant les test unitaire dans un processus séparé.
    Se serait en effet l'idéal de créer un processus pour chaque test pour avoir un environnement "propre".

    Par processus séparés, je présume qu'un fork suffit (?) est-il nécessaire de faire en plus un execv ?

    Mais je me demande si cela ne peut pas être "très lourd", si on a beaucoup de tests, ceci ne peut-il pas devenir relativement coûteux ?
    Surtout si les tests sont petits (?).
    D'un autre côté, ceci permet de continuer les tests même si le test plante.

    Je pourrais en effet faire un fork par fichier de tests et communiquer via un pipe avec le programme principal.
    Comme cela, même si un fichier de test est mal conçu, les autres fichiers ne planteront pas.
    Mais pour les tests en eux-mêmes… je pense qu'il serait mieux de juste créer un seul fork puis de le relancer si jamais il venait à planter.


    et un assert à chque test de retour de fonction
    Je vois.
    Par contre, cela me semble dur de recueillir plus d'informations sur l'échec du test.

    Un flow d'execution simple
    Quitte à faire des processus séparés, pourquoi ne pas tenter de lancer plusieurs tests à la fois ?


    En tout cas ce sont des pistes intéressantes :
    • prise en charge des signaux ;
    • processus pour séparer les tests ;
    • parallélisation des tests ;
    • enregistrer les std::cerr/std::cout.

  11. #11
    Rédacteur/Modérateur

    Avatar de yahiko
    Homme Profil pro
    Développeur
    Inscrit en
    Juillet 2013
    Messages
    1 423
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Alpes Maritimes (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur

    Informations forums :
    Inscription : Juillet 2013
    Messages : 1 423
    Points : 8 699
    Points
    8 699
    Billets dans le blog
    43
    Par défaut
    Les assertions sont utiles à la fois pour les tests unitaires, mais également dans le code "fonctionnel".

    A l'appui, je me permets assez immodestement d'auto-citer deux posts de mon blog :
    Tutoriels et FAQ TypeScript

  12. #12
    Membre éclairé

    Profil pro
    Inscrit en
    Décembre 2013
    Messages
    393
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2013
    Messages : 393
    Points : 684
    Points
    684
    Par défaut
    Citation Envoyé par skeud Voir le message
    Les assert doivent etre au niveau des tests, il ne doit pas y avoir d'assert dans un programme:
    _ un assert dans un programme équivaut à un exit(0); avec un message en plus.
    _ un assert sert à arréter le programme afin qu'il ne parte pas en vrille, il vaut mieux utiliser les exceptions dans ce cas ou un retour d'erreur.
    Je ne suis pas d'accord sur ce point (cf le blog de lmghs sur la programmation par contrat).
    Les assert ne sont lancés qu'en mode debug, donc ils permettent de retrouver directement dans le code ce qui produit une assertion (ie ce qui ne respecte pas les contrats). C'est particulièrement utile pour tester les -conditions des fonctions (utiliser une exception dans ce cas relève de la programmation défensive, ce qui n'est pas forcement une bonne approche. Quand a la gestion des erreurs par retour de fonction, c'est une très mauvaise pratique, largement débattue - cf l'article "exception vs retour de fonction", traduit par LittleWhite de mémoire).

    Les assert ne servent pas a la gestion des erreurs "coté utilisateur", mais uniquement pour les erreurs de programmation.
    En particulier, je recommande en general d'utiliser un assert a chaque fois que l'on déréférence un pointeur ou utilise l’opérateur [] (sauf si on fait un if).

    Utiliser les assert dans des tests n'est pas non plus une bonne idée. S'ils sont au milieu du test, le test ne sera pas fini. S'ils sont a la fin (comme tu le suggères), il ne sert a rien, un retour ou un cout/cerr sera aussi efficace.

    Créer un test unitaire par fonction est peut être un peu lourd. J'ai l'habitude de faire un test par classe, chaque fonction étant testé par un ou plusieurs tests. Mais au final, peut importe. Le plus important est que l'on retrouve facilement la source de l'erreur (il ne faudrait pas que le test n'affiche que le nom de la fonction Il faut affiche la classe, la fonction, le use case, le seed si on utilise un nombre aleatoire, etc. Bref, tout ce qu'il faut pour reproduire)

  13. #13
    Inactif  


    Homme Profil pro
    Doctorant sécurité informatique — Diplômé master Droit/Économie/Gestion
    Inscrit en
    Décembre 2011
    Messages
    9 012
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 31
    Localisation : France, Loire (Rhône Alpes)

    Informations professionnelles :
    Activité : Doctorant sécurité informatique — Diplômé master Droit/Économie/Gestion
    Secteur : Enseignement

    Informations forums :
    Inscription : Décembre 2011
    Messages : 9 012
    Points : 23 211
    Points
    23 211
    Par défaut
    Les assertions sont utiles à la fois pour les tests unitaires, mais également dans le code "fonctionnel"..
    Là, il parle d'assertion au sens large, d'ailleurs j'ai d'ailleurs l'impression qu'ils déconseillent l'utilisation d'assert (pour cette situation).

    Par assertions on parlait de l'utilisation d'assert. Mais oui, on est pas contre un if( param > 10){ return -1; }.

  14. #14
    Membre éclairé

    Profil pro
    Inscrit en
    Décembre 2013
    Messages
    393
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2013
    Messages : 393
    Points : 684
    Points
    684
    Par défaut
    Citation Envoyé par Neckara Voir le message
    Se serait en effet l'idéal de créer un processus pour chaque test pour avoir un environnement "propre".

    Par processus séparés, je présume qu'un fork suffit (?) est-il nécessaire de faire en plus un execv ?
    Remarque sur ce point : dans les outils de TU que j'ai utilisé, chaque test est un programme (par classe ou par fonction), qui sont lancés par un script. Le script lit ensuite la sortie pour détecter si les tests ont réussit ou non. Donc pas besoin de fork ou équivalent, chaque test est indépendant des autres. Donc un environnement "propre" n'est pas un problème a ce niveau.

    Par contre, il y a quand même (généralement) un problème d'environnement "propre" : comme les tests sont lancés sur un machine dédiée (machine de dev ou de test), il reste souvent des "traces" des précédents tests ou une configuration de la machine qui est spécifique et ne correspond pas a la configuration que l'on retrouvera habituellement sur les machines des utilisateurs finaux. (un exemple qui m'est arrivé récemment, c'est un build qui échoue et une dll qui n'est pas créée... mais il restait la dll du précédent build et les tests échouaient alors que les bugs avaient été fixés). Il est donc important de lancer les tests (en particulier de déploiement) sur un environnement vierge (par exemple, une image disque vierge d'une VM)

  15. #15
    Inactif  


    Homme Profil pro
    Doctorant sécurité informatique — Diplômé master Droit/Économie/Gestion
    Inscrit en
    Décembre 2011
    Messages
    9 012
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 31
    Localisation : France, Loire (Rhône Alpes)

    Informations professionnelles :
    Activité : Doctorant sécurité informatique — Diplômé master Droit/Économie/Gestion
    Secteur : Enseignement

    Informations forums :
    Inscription : Décembre 2011
    Messages : 9 012
    Points : 23 211
    Points
    23 211
    Par défaut
    Citation Envoyé par mintho carmo Voir le message
    Quand a la gestion des erreurs par retour de fonction, c'est une très mauvaise pratique, largement débattue - cf l'article "exception vs retour de fonction", traduit par LittleWhite de mémoire).
    Personnellement, je n'aime pas quand les fonctions lancent des exceptions à tout va.

    Je préfère avoir un retour de fonction et décider de ce que je dois faire localement.
    Si je dois faire un try/catch pour chaque ligne, je ne vois pas l'avantage à utiliser des exceptions.
    Si je dois avoir plusieurs lignes, je n'aime pas trop traiter l'exception "aussi loin dans le code".
    De plus, avoir des codes de retours permet de montrer qu'une fonction peut échouer, ce qui est "invisible" avec une exception.

    Le dernier exemple est tout de même relativement biaisé :
    • avec le code de retours, on ne rencontre pas un tel cas tous les jours et on peut découper en petites fonctions ;
    • dans le code code avec exception, on ne traite au final jamais l'exception .


    C'est personnel, mais je n'aime pas trop les exceptions.

    Enfin bref, je vais essayer de ne pas trop dévier.


    Citation Envoyé par mintho carmo Voir le message
    Remarque sur ce point : dans les outils de TU que j'ai utilisé, chaque test est un programme (par classe ou par fonction), qui sont lancés par un script. Le script lit ensuite la sortie pour détecter si les tests ont réussit ou non. Donc pas besoin de fork ou equivalent, chaque test est independant des autres. Donc un environnement "propre" n'est pas un problème a ce niveau.
    Enfin ton script remplace le fork + execv.
    Le problème d'un programme séparé, c'est la récupération des données, lire la sortie standard n'est pas forcément le plus génial .

    Je pense que je vais partir "à la bourrin" en lançant un fork par tests. Je pense que le fork suffira à garantir une certaine indépendance.

    Citation Envoyé par mintho carmo Voir le message
    Il est donc important de lancer les tests (en particulier de deploiment) sur un envirronment vierge (par exemple, une image disque vierge d'une VM)
    Je ne penses pas que j'irais jusque là .

  16. #16
    Membre éclairé

    Profil pro
    Inscrit en
    Décembre 2013
    Messages
    393
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2013
    Messages : 393
    Points : 684
    Points
    684
    Par défaut
    Citation Envoyé par Neckara Voir le message
    Personnellement, je n'aime pas quand les fonctions lancent des exceptions à tout va.

    Je préfère avoir un retour de fonction et décider de ce que je dois faire localement.
    Si je dois faire un try/catch pour chaque ligne, je ne vois pas l'avantage à utiliser des exceptions.
    Si je dois avoir plusieurs lignes, je n'aime pas trop traiter l'exception "aussi loin dans le code".
    De plus, avoir des codes de retours permet de montrer qu'une fonction peut échouer, ce qui est "invisible" avec une exception.

    Le dernier exemple est tout de même relativement biaisé :
    • avec le code de retours, on ne rencontre pas un tel cas tous les jours et on peut découper en petites fonctions ;
    • dans le code code avec exception, on ne traite au final jamais l'exception .


    C'est personnel, mais je n'aime pas trop les exceptions.
    Comme beaucoup de développeur qui ont l'habitude du C
    (je suppose que tu vois a quels articles je fais référence ?)
    Le problème des retours de fonction est qu'ils ont tendance a être ignorée, ce qui conduit le programme dans un état instable et donc complique le débogage (comportement indéterminé, qui plante n'importe ou, mais jamais a la bonne ligne dans le code)

    Mais tu n'es pas sensé avoir une exception a chaque ligne de code. Comme leur nom l'indique, les exceptions doivent être exceptionnel. S'il y a beaucoup d'exceptions, il y a probablement un mauvais usage (par exemple, utiliser les exceptions pour tester les pré-conditions). Beaucoup de fonctions devraient être noexcept ou exception-neutral (ie ne lance pas d'exception, mais peut appeler une fonction qui en lance)

  17. #17
    Membre émérite
    Avatar de skeud
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Juin 2011
    Messages
    1 091
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 34
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juin 2011
    Messages : 1 091
    Points : 2 724
    Points
    2 724
    Billets dans le blog
    1
    Par défaut
    Personnellement j'utilise les exception dans le cas d'erreur critique (ne pouvant conduire à une bonne exécution de la suite du programme, comme un pointeur vide, ou de mauvaise donnée d'entrée).

    Et le retour d'erreur quand c'est juste un ptit warning (donnée non présente, mais ne bloquant pas le programme).

    Ensuite le gros avantage des exceptions est de pouvoir retrouvé l'origine (ie endroit du code qui a soulevé l'exception). Sauf qu'en cas d'exception lever, avec une mauvaise gestion, il est facile de sauter des parties du workflow, et donc de conduire à des problèmes, c'est souvent pour cette raison que les exception sont pas très conseillées.

    Mais avec une gestion d'exception sur différent niveau (error, warning) et une bonne gestion des catcheur (le jeu de mot pourris.XD) on doit pouvoir s'en tirer pour avoir un programme simple, qui va directement indiquer l'endroit de l'erreur.

    En gros, pour moi:
    _ Exception: pratique pour debugguer, ou retrouver via une stack trace les erreurs en prod
    _ Retour erreur: pratique pour avoir une gestion des erreur mineurs du à l'utilisateur, ou les fichiers en entré, sans crasher le programme.
    Pas de solution, pas de probleme

    Une réponse utile (ou +1) ->
    Une réponse inutile ou pas d'accord -> et expliquer pourquoi
    Une réponse à votre question


  18. #18
    Rédacteur/Modérateur


    Homme Profil pro
    Network game programmer
    Inscrit en
    Juin 2010
    Messages
    7 113
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : Canada

    Informations professionnelles :
    Activité : Network game programmer

    Informations forums :
    Inscription : Juin 2010
    Messages : 7 113
    Points : 32 960
    Points
    32 960
    Billets dans le blog
    4
    Par défaut
    Citation Envoyé par mintho carmo Voir le message
    Le problème des retours de fonction est qu'ils ont tendance a être ignorée, ce qui conduit le programme dans un état instable et donc complique le débogage
    Le problème avec les méthodes, c'est qu'on peut les appeler avec des paramètres farfelus.
    Tu peux aller loin comme ça.

    Les codes erreurs, c'est justement pour être vérifié.
    Si tu cotoies des personnes qui ont tendance à écrire int i = 0; delete &i;, faut pas nous en vouloir.
    Pensez à consulter la FAQ ou les cours et tutoriels de la section C++.
    Un peu de programmation réseau ?
    Aucune aide via MP ne sera dispensée. Merci d'utiliser les forums prévus à cet effet.

  19. #19
    Membre éclairé

    Profil pro
    Inscrit en
    Décembre 2013
    Messages
    393
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2013
    Messages : 393
    Points : 684
    Points
    684
    Par défaut
    Citation Envoyé par Bousk Voir le message
    Le problème avec les méthodes, c'est qu'on peut les appeler avec des paramètres farfelus.
    C'est justement le boulot des assert de vérifier les pré-conditions. Si l'utilisateur appelle une méthode avec des paramètres farfelus (ie qui ne respectent pas le contrat), alors par définition cela produit un UB (ie c'est une erreur de programmation, qui doit être détectée avec des assert).

    Pour avoir un retour de fonction qui est correct, il ne faut pas avoir de UB, donc que les paramètres respectent le contrat. Dis autrement, cela veut dire que pour avoir un fonction qui accepte un paramètre invalide (mais qui ne produit pas de UB), il faut que ce paramètre soit pris en charge par la fonction (et donc soit dans le contrat).

    Un exemple concret : si tu écris une fonction open qui prend en paramètre un nom de fichier, tu peux faire 2 choix sur le type de contrat :

    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
    // contrat strict: on suppose que la chaîne est valide
    void open(const string &filename) {
        assert(!filename.empty()); // non respect du contrat
        // code pour ouvrir le fichier
        // si filename n'est pas valide, on a un UB et on ne peut garantir la valeur retournée
    }
     
    // contrat large: une chaine vide est valide
    error_code open(const string &filename) {
        if (filename.empty()) { // une chaine vide est une entree valide
            return ERR_INVALID_FILENAME;
            // si filename n'est pas valide, on peut garantir la valeur retournée
        }
        // code pour ouvrir le fichier
    }
    Pour le problème de fichier invalide (mais avec un filename correcte), on est dans l'erreur de runtime, donc qui peut être géré par une exception. Tout dépend comment c'est gérer et a quel moment. Si par exemple on a une UI qui demande le nom de fichier, on peut avoir une vérification dans cet UI, ce qui va permettre d’appeler la fonction open ou une fonction create. Dans ce cas, dans ces fonctions, on aura un assert (puisque l'appel de ces fonctions sera garantie valide).
    Ou au contraire, on peut avoir une fonction open qui ouvre un fichier s'il existe ou le crée si nécessaire.

    Le choix des pre-conditions (et donc de l'utilisation de assert et des exceptions) est une question de design (et donc de choix plus ou moins personnel). Mais plus le contrat est permissif, plus la fonction aura de fonctionnalités, ce qui peut entrer en conflit avec le SRP.

    (et pour les retours de fonction vs exception, cf l'article que j'ai cité)

  20. #20
    Inactif  


    Homme Profil pro
    Doctorant sécurité informatique — Diplômé master Droit/Économie/Gestion
    Inscrit en
    Décembre 2011
    Messages
    9 012
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 31
    Localisation : France, Loire (Rhône Alpes)

    Informations professionnelles :
    Activité : Doctorant sécurité informatique — Diplômé master Droit/Économie/Gestion
    Secteur : Enseignement

    Informations forums :
    Inscription : Décembre 2011
    Messages : 9 012
    Points : 23 211
    Points
    23 211
    Par défaut
    Citation Envoyé par skeud Voir le message
    Personnellement j'utilise les exception dans le cas d'erreur critique (ne pouvant conduire à une bonne exécution de la suite du programme, comme un pointeur vide, ou de mauvaise donnée d'entrée).
    Ouais, je renvois aussi parfois des exceptions quand c'est vraiment critique, mais cela reste en effet assez rare.

    Citation Envoyé par mintho carmo Voir le message
    Mais tu n'es pas sensé avoir une exception a chaque ligne de code. Comme leur nom l'indique, les exceptions doivent être exceptionnel. S'il y a beaucoup d'exceptions, il y a probablement un mauvais usage
    Je pense que c'est le Java qui m'a un peu dégoûté des exceptions .

Discussions similaires

  1. Le langage Java est-il adapté pour les jeux vidéo ?
    Par Invité dans le forum Développement 2D, 3D et Jeux
    Réponses: 637
    Dernier message: 05/02/2021, 22h38
  2. La dématérialisation des supports a-t-elle de l'avenir pour les Jeux-vidéos ?
    Par raptor70 dans le forum Développement 2D, 3D et Jeux
    Réponses: 45
    Dernier message: 12/04/2011, 11h01
  3. Réponses: 0
    Dernier message: 01/09/2009, 11h00
  4. Résolution des images pour les jeux vidéos
    Par YuGiOhJCJ dans le forum Développement 2D, 3D et Jeux
    Réponses: 4
    Dernier message: 04/04/2006, 12h24

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