Comment attirer l'attention du programmeur dans certaines conditions?


13

Commençons par un exemple.

Disons que j'ai une méthode appelée exportqui dépend fortement du schéma DB. Et par «dépend fortement», je veux dire que je sais que l'ajout d'une nouvelle colonne à une certaine table conduit souvent (très souvent) au exportchangement de méthode correspondant (en général, vous devez également ajouter le nouveau champ aux données d'exportation).

Les programmeurs oublient souvent de changer de exportméthode car il n'est pas vraiment clair que vous devriez même y regarder. Mon objectif est de forcer le programmeur à prendre explicitement une décision pour déterminer s'il a oublié de regarder la exportméthode ou s'il ne veut tout simplement pas ajouter un champ aux données d'exportation. Et je cherche la solution de conception pour ce problème.

J'ai deux idées, mais les deux ont des défauts.

Emballage intelligent «Tout lire»

Je peux créer le wrapper intelligent qui s'assure que toutes les données sont lues explicitement.

Quelque chose comme ça:

def export():
    checker = AllReadChecker.new(table_row)

    name    = checker.get('name')
    surname = checker.get('surname')
              checker.ignore('age') # explicitly ignore the "age" field

    result = [name, surname] # or whatever

    checker.check_now() # check all is read

    return result

Ainsi, checkeraffirme si table_rowcontient d'autres champs qui n'ont pas été lus. Mais tout cela semble plutôt lourd et (peut-être) affecte la performance.

«Vérifiez cette méthode»

Je peux simplement créer le plus unitaire qui se souvient du dernier schéma de table et échoue à chaque fois que la table est modifiée. Dans ce cas, le programmeur verrait quelque chose comme «n'oubliez pas de vérifier la exportméthode». Pour cacher l'avertisseur, le programmeur vérifierait (ou non - c'est un problème) exportet corrigerait manuellement (c'est un autre problème) le test en y ajoutant de nouveaux champs.

J'ai quelques autres idées mais elles sont trop gênantes à mettre en œuvre ou trop difficiles à comprendre (et je ne veux pas que le projet devienne un puzzle).


Le problème ci-dessus n'est qu'un exemple de la classe plus large de problèmes que je rencontre de temps en temps. Je veux lier certains morceaux de code et / ou infrastructure, donc changer l'un d'eux alerte immédiatement le programmeur pour qu'il en vérifie un autre. Habituellement, vous avez des outils simples comme extraire une logique commune ou écrire des tests fiables, mais je recherche l'outil pour des cas plus complexes: peut-être certains modèles de conception dont je suis maintenant au courant.


Pouvez-vous peut-être générer exportsur la base du schéma?
coredump

Il ne peut pas être généré de manière externe, c'est pourquoi je devrais demander au programmeur de regarder le code et de prendre une décision.
Vadim Pushtaev du

Serait-ce une solution pour ajouter deux listes de noms de champs (export et dont-export) à la classe d'exportation, et avoir un test unitaire qui vérifie que ces deux listes englobent l'ensemble complet des champs de la base de données?
Sjoerd Job Postmus

Pouvez-vous générer automatiquement un test qui vérifie si exporttout ce dont vous avez réellement besoin?
biziclop

1
un commentaire dans le code source une solution trop simpliste? Habituellement, les choses sont manquées car il n'y a pas de rappel, un commentaire réglerait cela.
gbjbaanb

Réponses:


11

Vous êtes sur la bonne voie avec votre idée de test unitaire, mais votre mise en œuvre est incorrecte.

Si le exportest lié au schéma et que le schéma a changé, il y a deux cas possibles:

  • Soit le exportfonctionne toujours parfaitement, car il n'a pas été affecté par une légère modification du schéma,

  • Ou ça casse.

Dans les deux cas, l'objectif de la génération est de rechercher cette régression possible. Un tas de tests - que ce soit des tests d'intégration, des tests système, des tests fonctionnels ou autre chose - garantissent que votre exportprocédure fonctionne avec le schéma actuel , indépendamment du fait qu'elle ait changé ou non depuis la validation précédente. Si ces tests réussissent, tant mieux. S'ils échouent, c'est un signe pour le développeur qu'il a peut-être manqué quelque chose, et une indication claire où chercher.

Pourquoi votre implémentation est-elle mauvaise? Eh bien, pour plusieurs raisons.

  1. Cela n'a rien à voir avec les tests unitaires ...

  2. ... et, en fait, ce n'est même pas un test.

  3. Le pire, c'est que la fixation du «test» nécessite, en fait, de changer réellement le «test», c'est-à-dire d'effectuer une opération qui n'a aucun rapport avec le export.

Au lieu de cela, en effectuant des tests réels pour la exportprocédure, vous vous assurez que le développeur corrigera le export.


Plus généralement, lorsque vous rencontrez une situation où un changement dans une classe nécessite toujours ou généralement un changement dans une classe complètement différente, c'est un bon signe que vous avez mal fait votre conception et que vous violez le principe de responsabilité unique.

Bien que je parle spécifiquement des classes, cela s'applique plus ou moins à d'autres entités. Par exemple, une modification du schéma de la base de données doit se refléter automatiquement dans votre code, par exemple via les générateurs de code utilisés par de nombreux ORM, ou au moins être facilement localisée: si j'ajoute une Descriptioncolonne à la Producttable et que je n'utilise aucun ORM ni générateur de code, Je m'attends au moins à effectuer un seul changement au sein de la Data.Productclasse du DAL, sans avoir besoin de parcourir toute la base de code et de trouver des occurrences de Productclasse dans, disons, la couche de présentation.

Si vous ne pouvez pas raisonnablement limiter le changement à un seul emplacement (soit parce que vous êtes dans un cas où cela ne fonctionne tout simplement pas, soit parce qu'il nécessite une énorme quantité de développement), alors vous créez un risque de régression . Quand je change de classe Aet que la classe Bquelque part dans la base de code cesse de fonctionner, c'est une régression.

Les tests réduisent le risque de régression et, ce qui est beaucoup plus important, vous montre l'emplacement d'une régression. C'est pourquoi, lorsque vous savez que des changements dans un emplacement provoquent des problèmes dans une partie complètement différente de la base de code, assurez-vous d'avoir suffisamment de tests qui déclenchent des alarmes dès qu'une régression apparaît à ce niveau.

Dans tous les cas, évitez de vous fier dans ces cas uniquement aux commentaires. Quelque chose comme:

// If you change the following line, make sure you also change the corresponding
// `measure` value in `Scaffolding.Builder`.

ne fonctionne jamais. Non seulement les développeurs ne le liront pas dans la plupart des cas, mais il finira souvent par être supprimé ou éloigné de la ligne concernée, et deviendra impossible à comprendre.


Oui, ce «test» n'est pas un test en effet, c'est une sorte de piège, une sorte de If you change the following line...qui fonctionne automatiquement et ne peut pas être simplement ignoré. Je n'ai jamais vu quelqu'un utiliser de tels pièges, d'où le doute.
Vadim Pushtaev

1
Le problème est la classe Bne cesse de travailler, il peut - être commence à travailler correctement. Mais je ne peux pas prédire l'avenir et je ne peux pas écrire de tests qui prédisent l'avenir, alors j'essaie de rappeler au développeur d'écrire ce nouveau test.
Vadim Pushtaev

@VadimPushtaev: en ce qui concerne les tests, «peut-être commence à mal fonctionner» et «arrête de fonctionner» est exactement la même chose. Vous ne pouvez pas prédire l'avenir, mais vous devez être en mesure de connaître exactement les exigences et de vérifier que ces exigences sont remplies par votre produit réel.
Arseni Mourzenko

Ouais, c'est une chose. Je veux en fait rappeler au développeur de réfléchir aux nouvelles exigences . Je veux juste faire signe de la main: «Bonjour, êtes-vous sûr de ne pas oublier l'exportation? Demandez au manager ou quelque chose comme ça, c'est un problème courant ».
Vadim Pushtaev

Merci quand même, votre réponse m'a aidé à organiser mes pensées et j'ai maintenant un certain plan.
Vadim Pushtaev

3

Il me semble que vos changements sont sous-spécifiés. Supposons que vous vivez dans un endroit qui n'a pas de codes postaux, vous n'avez donc pas de colonne de code postal dans la table d'adresses. Ensuite, les codes postaux sont introduits, ou vous commencez à traiter avec des clients qui vivent là où il y a des codes postaux, et vous devez ajouter cette colonne au tableau.

Si l'élément de travail indique simplement "ajouter une colonne de code postal à la table d'adresses", alors oui, l'exportation sera interrompue ou, du moins, n'exportera pas de codes postaux. Mais qu'en est-il de l'écran de saisie utilisé pour saisir les codes postaux? Le rapport qui répertorie tous les clients et leurs adresses? Il y a des tonnes de choses qui doivent changer lorsque vous ajoutez cette colonne. Le travail de se souvenir de ces choses est important - vous ne devriez pas compter sur des artefacts de code aléatoires pour "piéger" les développeurs dans la mémoire.

Lorsque la décision est prise d'ajouter une colonne significative (c'est-à-dire, pas seulement une recherche totale ou dénormalisée mise en cache ou une autre valeur qui n'appartient pas à une exportation ou un rapport ou sur un écran de saisie), les éléments de travail créés doivent inclure TOUTES les modifications nécessaire - ajout de la colonne, mise à jour du script de remplissage, mise à jour des tests, mise à jour de l'exportation, des rapports, des écrans de saisie, etc. Ceux-ci ne peuvent pas tous être attribués (ou récupérés) à la même personne, mais ils doivent tous être effectués.

Parfois, les développeurs choisissent d'ajouter eux-mêmes des colonnes dans le cadre de l'implémentation de modifications plus importantes. Par exemple, quelqu'un peut avoir écrit un élément de travail pour ajouter quelque chose à un écran de saisie et un rapport, sans penser à la façon dont il est mis en œuvre. Si cela se produit souvent, vous devrez décider si votre additionneur d'élément de travail doit connaître les détails d'implémentation (afin de pouvoir ajouter tous les bons éléments de travail) ou si les développeurs doivent être conscients que l'élément de travail- l'additionneur laisse parfois les choses de côté. Si c'est le dernier, alors vous avez besoin d'une culture de «ne changez pas simplement le schéma; arrêtez-vous et réfléchissez à ce que cela affecte."

S'il y avait beaucoup de développeurs et que cela se produisait plus d'une fois, je mettrais en place une alerte de consignation pour que le chef d'équipe ou une autre personne senior soit alerté des changements de schéma. Cette personne pourrait alors rechercher des éléments de travail connexes pour faire face aux conséquences de leur changement de schéma et, si les éléments de travail étaient manquants, non seulement les ajouter, mais aussi éduquer ceux qui les avaient exclus du plan.


2

Presque toujours, lors de la création d'une exportation, je crée également une importation correspondante. Comme j'ai d'autres tests qui remplissent entièrement la structure de données exportée, je peux ensuite créer un test unitaire aller-retour qui compare un original entièrement rempli avec une copie exportée puis importée. S'ils sont identiques, l'exportation / importation est terminée; s'ils ne sont pas identiques, le test unitaire échoue et je sais que le mécanisme d'exportation doit être mis à jour.


cela peut vérifier que chaque objet est enregistré et rechargé à partir de db. Il ne couvre pas le cas où un champ db existant n'a pas de champ objet correspondant.
k3b

@ k3b true. Au moins dans mes systèmes, cela se produit généralement si quelque chose n'est plus utilisé mais n'a pas été supprimé du schéma, ce qui est en dehors de la portée du mécanisme d'exportation - l'unité teste la persistance de l'objet vérifie si chaque champ d'un objet est persisté, mais je ne teste pas les colonnes inutilisées car cela n'aurait aucun effet observable sur la fonction du système.
Pete Kirkham
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.