Est-ce une bonne idée de mesurer les performances d'une méthode en utilisant le délai de test unitaire?

14

Dans un projet où il existe des exigences non fonctionnelles qui spécifient le temps d'exécution maximal pour une action spécifique, l'AQ doit vérifier les performances de cette action sur une machine dédiée en utilisant du matériel précis sous une charge précise, le matériel et la charge étant spécifiés dans les exigences.

D'un autre côté, certaines modifications erronées du code source peuvent gravement affecter les performances. Noter cet impact négatif tôt , avant que le code source n'atteigne le contrôle de source et soit vérifié par le département QA, pourrait être bénéfique en termes de temps perdu par le département QA signalant le problème, et par le développeur le corrigeant plusieurs validations plus tard.

Pour ce faire, est-ce une bonne idée:

  • Utiliser des tests unitaires pour avoir une idée du temps passé à exécuter la même action² n fois,

  • Pour utiliser le délai d'expiration par test via l' [TestMethod, Timeout(200)]attribut en C #?

J'attends plusieurs problèmes avec cette approche:

  • Conceptuellement , les tests unitaires ne sont pas vraiment pour ça: ils sont censés tester une petite partie d'un code, rien de plus: ni la vérification d'une exigence fonctionnelle, ni un test d'intégration, ni un test de performance.

  • Le délai d'expiration des tests unitaires dans Visual Studio mesure-t-il vraiment ce qui devrait être mesuré, en tenant compte du fait que l'initialisation et le nettoyage sont inexistants pour ces tests ou sont trop courts pour affecter les résultats?

  • Mesurer les performances de cette façon est moche. Exécuter un benchmark sur n'importe quelle machine¹ indépendamment du matériel, de la charge, etc., c'est comme faire un benchmark qui montre qu'un produit de base de données est toujours plus rapide qu'un autre. D'un autre côté, je ne m'attends pas à ce que ces tests unitaires soient un résultat définitif, ni quelque chose qui est utilisé par le département QA . Ces tests unitaires seront utilisés uniquement pour donner une idée générale des performances attendues et essentiellement pour alerter le développeur que sa dernière modification a cassé quelque chose, affectant gravement les performances .

  • Le développement piloté par les tests (TDD) est impossible pour ces tests. Comment échouerait-il, en premier lieu, avant de commencer à implémenter du code?

  • Trop de tests de performances affecteront le temps requis pour exécuter les tests, donc cette approche est limitée aux actions courtes uniquement.

Compte tenu de ces problèmes, je trouve toujours intéressant d'utiliser de tels tests unitaires s'ils sont combinés avec les mesures de performances réelles par le service QA.

Ai-je tort? Y a-t-il d'autres problèmes qui rendent totalement inacceptable l'utilisation de tests unitaires pour cela?

Si je me trompe, quelle est la bonne façon d'alerter le développeur qu'un changement de code source a gravement affecté les performances, avant que le code source n'atteigne le contrôle de code source et soit vérifié par le service d'assurance qualité?


¹ En fait, les tests unitaires ne devraient s'exécuter que sur des PC de développeur ayant des performances matérielles comparables, ce qui réduit l'écart entre les machines les plus rapides qui ne pourront jamais échouer au test de performances et les machines les plus lentes qui ne réussiront jamais à le réussir.

² Par action, je veux dire un morceau de code assez court qui passe quelques millisecondes à s'exécuter.

Arseni Mourzenko
la source

Réponses:

3

Nous utilisons également cette approche, c'est-à-dire que nous avons des tests qui mesurent le temps d'exécution sous un scénario de charge défini sur une machine donnée. Il peut être important de souligner que nous ne les incluons pas dans les tests unitaires normaux. Les tests unitaires sont essentiellement exécutés par chaque développeur sur une machine de développeur avant de valider les modifications. Voir ci-dessous pourquoi cela n'a aucun sens pour les tests de performances (au moins dans notre cas). Au lieu de cela, nous exécutons des tests de performances dans le cadre des tests d'intégration.

Vous avez correctement fait remarquer que cela ne devait pas exclure la vérification. Nous ne supposons pas que notre test est un test de l'exigence non fonctionnelle. Au lieu de cela, nous le considérons comme un simple indicateur de problème potentiel.

Je ne suis pas sûr de votre produit, mais dans notre cas, si les performances sont insuffisantes, cela signifie que beaucoup de travail est nécessaire pour "corriger" cela. Ainsi, le délai d'exécution, lorsque nous laissons cela entièrement à l'AQ, est horrible. En outre, les correctifs de performances auront de graves répercussions sur une grande partie de la base de code, ce qui annule le travail de contrôle qualité précédent. Dans l'ensemble, un flux de travail très inefficace et insatisfaisant.

Cela étant dit, voici quelques points à vos problèmes respectifs:

  • conceptuellement: il est vrai que ce n'est pas de cela qu'il s'agit. Mais tant que tout le monde sait que le test n'est pas censé vérifier quoi que ce soit que l'AQ devrait faire, ça va.

  • Visual Studio: je ne peux rien dire à ce sujet, car nous n'utilisons pas le framework de test unitaire de VS.

  • Machine: dépend du produit. Si votre produit est quelque chose de développé pour les utilisateurs finaux avec des machines de bureau individuelles personnalisées, il est en fait plus réaliste d'exécuter les tests sur les machines de différents développeurs. Dans notre cas, nous livrons le produit pour une machine avec une spécification donnée et nous exécutons ces tests de performance uniquement sur une telle machine. En effet, il n'y a pas grand intérêt à mesurer les performances sur votre machine de développeur double cœur, lorsque le client exécutera finalement 16 cœurs ou plus.

  • TDD: Bien que l'échec initial soit typique, ce n'est pas un must. En fait, l'écriture précoce de ces tests en fait plus un test de régression qu'un test unitaire traditionnel. Que le test réussisse tôt ne pose aucun problème. Mais vous obtenez l'avantage que chaque fois qu'un développeur ajoute des fonctionnalités qui ralentissent les choses, car il n'était pas au courant des exigences de performances non fonctionnelles, ce test TDD le détectera. Cela arrive beaucoup et c'est une rétroaction impressionnante. Imaginez que dans votre travail quotidien: vous écrivez du code, vous l'engagez, vous allez déjeuner et quand vous êtes de retour, le système de construction vous dit que ce code lorsqu'il est exécuté dans un environnement à forte charge est trop lent. C'est assez agréable pour moi d'accepter, que le test TDD n'est pas initialement échoué.

  • Exécution: Comme mentionné, nous n'exécutons pas ces tests sur des machines de développement, mais plutôt dans le cadre du système de génération dans une sorte de test d'intégration.

Franc
la source
3

Je suis principalement en phase avec votre réflexion. Je mets juste mon raisonnement en flux indépendant.

1. Faites-le fonctionner avant de le rendre meilleur / plus rapide
Avant que le code ne fournisse une mesure de performance (sans parler de garantir), il doit d'abord être rendu correct, c'est-à-dire le faire fonctionner fonctionnellement. L'optimisation d'un code fonctionnellement incorrect n'est pas seulement une perte de temps, mais crée des obstacles au développement.

2. Les performances d'un système n'ont de sens que sur un système complet
En règle générale, toute performance significative dépend toujours d'une infrastructure donnée et ne doit être vue que sous un système complet. Par exemple, pendant le test simulé si le module reçoit des réponses d'un fichier texte local, mais dans l'environnement de production, il récupère de la base de données, votre ancien

3. La mise à l'échelle des performances doit être effectuée par objectif
Une fois que vous avez le système fonctionnel, vous devez analyser les performances du système et trouver des goulots d'étranglement pour comprendre vous devez augmenter les performances. Essayer aveuglément d'optimiser chaque méthode avant même de connaître les performances d'un système complet peut entraîner une quantité de travail inutile (optimisation des méthodes sans importance) et créer votre code inutilement gonflé.

Je ne connais pas les fonctionnalités de Visual studio, mais vous avez généralement besoin d'un outil de profilage plus large.

Dipan Mehta
la source
2

J'ai eu une tâche similaire il y a quelque temps et la solution finale se situait quelque part entre les tests unitaires et les tests de performances automatisés à part entière.

Quelques considérations sans ordre particulier, qui peuvent être utiles:

  • Les tests de performance par QA exigeaient beaucoup de main-d'œuvre et avaient leur propre calendrier (disons, une fois dans l'itération), donc le contrôle du code source n'était pas un problème.
  • Notre système était grand et modulaire, les tests unitaires étaient trop granulaires pour nos besoins, et nous avons créé des tests unitaires «gras» spéciaux soigneusement conçus pour déclencher des problèmes de performance dans les domaines d'intérêt spécifiques (ils ont également été classés, mais c'est un détail d'implémentation).
  • Les contraintes habituelles pour les tests unitaires s'appliquent toujours: elles doivent être petites, rapides et précises.
  • Pour exclure l'influence du framework de test, ils étaient exécutés par un wrapper spécial, nous savions donc exactement combien de temps prend l'opération donnée.
  • Il est possible de les écrire avant la fin de l'implémentation réelle (les résultats peuvent être non pertinents ou utiles, selon le processus, peut-être que les développeurs expérimentent toujours l'implémentation et aimeraient voir comment cela se passe globalement).
  • Ils étaient exécutés par le serveur CI après chaque génération, donc la durée totale d'exécution devrait être maintenue relativement courte (si ce n'est pas le cas, il devient beaucoup plus difficile d'identifier le changement exact qui a déclenché le problème).
  • Le serveur CI était puissant et avait son matériel corrigé, nous avons donc compté cela comme une machine dédiée (il est possible d'utiliser un serveur vraiment dédié en utilisant un agent de build distant).
  • L'encapsuleur de test a collecté toutes les informations pertinentes (spécifications matérielles, noms / catégories de test, charge système, temps écoulé, etc.) et les a exportées sous forme de rapports ou dans la base de données.
  • Nous avons eu un gadget pour que JIRA tire ces rapports et dessine de jolis graphiques par nom / catégorie / numéro de build avec quelques contrôles (superposition de la version précédente à la version actuelle, etc.), afin que les développeurs puissent voir rapidement leur impact et que les gestionnaires puissent avoir un aperçu (un peu de rouge, tout vert, vous savez, c'est important pour eux).
  • Il a été possible d'analyser l'évolution du projet dans le temps en utilisant les statistiques collectées.

Donc, à la fin, nous avions un système évolutif, flexible et prévisible que nous pouvons régler rapidement pour nos besoins particuliers. Mais cela a demandé un certain effort de mise en œuvre.

Revenons aux questions. Sur le plan conceptuel , les tests unitaires ne sont pas faits pour cela, mais vous pouvez tirer parti des fonctionnalités de votre infrastructure de test. Je n'ai jamais considéré les délais d'attente de test comme un moyen de mesurer, c'est juste un filet de sécurité pour les blocages et autres choses du genre. Mais si votre approche actuelle fonctionne pour vous, continuez à l'utiliser, soyez pratique. Vous pouvez toujours aller plus loin si le besoin s'en fait sentir.

Oleg Kolosov
la source
0

Je pense que tu vas bien. C'est exactement le point d'avoir des délais d'attente de test unitaire: pour vérifier si quelque chose avance, beaucoup plus longtemps qu'il ne le devrait. Il y a des limites à cette approche, mais vous semblez déjà les connaître, donc tant que vous gardez ces limites à l'esprit, je ne vois pas de problème.

Mike Baranczak
la source