web-dev-qa-db-fra.com

Devrait-il être "Arrange-Assert-Act-Assert"?

En ce qui concerne le schéma de test classique de Arrange-Act-Assert , je me trouve souvent en train d'ajouter une contre-affirmation qui précède Act. De cette façon, je sais que l’affirmation passagère est vraiment passagère à la suite de l’action. 

Je pense que cela ressemble au rouge dans le refactor rouge-vert, où ce n'est que si j'ai vu la barre rouge au cours de mes tests que je sais que la barre verte signifie que j'ai écrit un code qui fait la différence. Si j’écris un test réussi, alors le code tout le satisfera; de même, en ce qui concerne Arrange-Assert-Act-Assert, si ma première assertion échoue, je sais que toute loi aurait adopté la dernière assertion, de sorte qu'elle ne vérifierait rien en réalité.

Vos tests suivent-ils ce schéma? Pourquoi ou pourquoi pas?

Update Clarification: l'assertion initiale est essentiellement l'opposé de l'assertion finale. Ce n'est pas une affirmation que Arrange a fonctionné; c'est une affirmation que la loi n'a pas encore fonctionné.

87
Carl Manaster

Voici un exemple.

public void testEncompass() throws Exception {
    Range range = new Range(0, 5);
    assertFalse(range.includes(7));
    range.encompass(7);
    assertTrue(range.includes(7));
}

Il se peut que j’ai écrit Range.includes() pour simplement renvoyer true. Je n'ai pas, mais je peux imaginer que je pourrais avoir. Ou j'aurais pu l'écrire de différentes manières. J'espère et je m'attends à ce qu'avec TDD, j'ai vraiment bien compris - que includes() fonctionne parfaitement - mais peut-être que non. La première affirmation est donc un contrôle de cohérence, afin de s’assurer que la deuxième affirmation est vraiment significative. 

Lisez par lui-même, assertTrue(range.includes(7)); dit: "affirmez que la plage modifiée comprend 7". Lu dans le contexte de la première affirmation, il est écrit: "affirme que l'invocation de englass () le fait en inclure 7. Et puisque englass est l'unité que nous testons, je pense que sa valeur est (petite).

J'accepte ma propre réponse; beaucoup d'autres ont mal interprété ma question sur le fait de tester la configuration. Je pense que c'est légèrement différent.

7
Carl Manaster

Ce n'est pas la chose la plus commune à faire, mais encore assez commune pour avoir son propre nom. Cette technique s'appelle Guard Assertion . Vous trouverez une description détaillée à la page 490 dans l'excellent livre xUnit Test Patterns de Gerard Meszaros (hautement recommandé).

Normalement, je n'utilise pas ce modèle moi-même, car je trouve plus correct d'écrire un test spécifique qui valide toute condition préalable que je ressens le besoin de garantir. Un tel test devrait toujours échouer en cas d'échec de la condition préalable, ce qui signifie que je n'ai pas besoin de l'inclure dans tous les autres tests. Cela donne une meilleure isolation des préoccupations, puisqu'un test élémentaire ne vérifie qu'une chose.

De nombreuses conditions préalables doivent être remplies pour un scénario de test donné. Par conséquent, vous aurez peut-être besoin de plus d'une assertion Guard. Au lieu de répéter ces opérations dans tous les tests, le fait de disposer d'un (et d'un seul) test pour chaque condition préalable permet de conserver votre code de test plus facilement, étant donné que vous aurez moins de répétitions de cette façon.

114
Mark Seemann

Il pourrait également être spécifié comme Arrange -Assume -Act-Assert.

Il existe un descripteur technique pour cela dans NUnit, comme dans l'exemple ci-dessous: http://nunit.org/index.php?p=theory&r=2.5.7

28
Ole Lynge

Un test Arrange-Assert-Act-Assert peut toujours être transformé en deux tests:

1. Arrange-Assert

et

2. Arrange-Act-Assert

Le premier test n'affirmera que ce qui a été mis en place dans la phase Arrangement, et le second test ne s'appliquera que pour ce qui s'est passé dans la phase Act.

Cela a l'avantage de donner un retour plus précis sur le fait que ce soit la phase Arrangement ou la phase Act qui a échoué, alors que dans le Arrange-Assert-Act-Assert original, ils sont regroupés et qu'il vous faudrait creuser plus profondément et examiner exactement quelle assertion a échoué et pourquoi. si c'était l'arrangement ou l'acte qui a échoué.

Cela répond également à l’intention de mieux tester les tests unitaires, car vous séparez votre test en unités plus petites et indépendantes.

Enfin, gardez à l'esprit que chaque fois que vous voyez des sections similaires d'Arrange dans des tests différents, vous devez essayer de les extraire en méthodes d'assistance partagées, afin que vos tests soient plus SECS et plus faciles à gérer dans le futur.

7
Sammi

Jetez un coup d'œil à l'entrée de Wikipedia sur Design by Contract . La sainte Trinité Arrange-Act-Assert est une tentative de coder certains des mêmes concepts et vise à prouver l'exactitude du programme. De l'article:

The notion of a contract extends down to the method/procedure level; the
contract for each method will normally contain the following pieces of
information:

    Acceptable and unacceptable input values or types, and their meanings
    Return values or types, and their meanings
    Error and exception condition values or types that can occur, and their meanings
    Side effects
    Preconditions
    Postconditions
    Invariants
    (more rarely) Performance guarantees, e.g. for time or space used

Il y a un compromis entre la quantité d'effort dépensé pour mettre cela en place et la valeur que cela ajoute. A-A-A est un rappel utile pour les étapes minimales requises, mais ne devrait décourager personne de créer des étapes supplémentaires.

1
David Clarke

Lancer une assertion de "vérification de cohérence" pour vérifier l'état avant d'effectuer l'action que vous testez est une technique ancienne. Je les écris habituellement comme échafaudage d’essai pour me prouver que l’essai fait ce que j’attendais, et les retire plus tard pour éviter les encombrements avec des échafaudages d’essai. Parfois, laisser l’échafaudage aide le test à servir de récit.

1
Dave W. Smith

J'ai déjà lu sur cette technique - peut-être même de votre part - mais je ne l'utilise pas; principalement parce que je suis habitué à la forme triple A pour mes tests unitaires.

Maintenant, je commence à être curieux et à poser quelques questions: comment écrivez-vous votre test, faites-vous échouer cette assertion en suivant un cycle rouge-vert-rouge-vert-refactor ou ajoutez-vous par la suite?

Vous échouez parfois, peut-être après avoir refacté le code? Qu'est-ce que cela vous dit? Peut-être que vous pourriez partager un exemple où cela a aidé. Merci. 

1
philant

En général, j'aime beaucoup «organiser, agir, affirmer» et l'utiliser comme norme personnelle. La seule chose qu’il ne me rappelle pas de faire, cependant, est de désorganiser ce que j’ai arrangé lorsque les assertions sont faites. Dans la plupart des cas, cela ne cause pas beaucoup d'ennuis, car la plupart des choses disparaissent automatiquement par un ramassage des ordures, etc. Si vous avez déjà établi des connexions avec des ressources externes, vous voudrez probablement les fermer lorsque vous aurez terminé. avec vos affirmations ou vous avez souvent un serveur ou une ressource chère quelque part qui tient à des connexions ou des ressources vitales qu’il devrait pouvoir donner à quelqu'un d’autre. Ceci est particulièrement important si vous êtes un de ces développeurs qui n'utilise pas TearDown ou TestFixtureTearDown pour nettoyer après un ou plusieurs tests. Bien entendu, "Arrange, Act, Assert" n'est pas responsable de mon incapacité à fermer ce que j'ouvre; Je mentionne seulement ce "gotcha" parce que je n'ai pas encore trouvé un bon synonyme "A-Word" pour "disposer" à recommander! Aucune suggestion?

1
John Tobler

Je l'ai déjà fait lors d'une enquête sur un test qui a échoué. 

Après avoir passé beaucoup de temps à gratter, j’ai déterminé que c’était parce que les méthodes appelées pendant "Arranger" ne fonctionnaient pas correctement. L'échec du test était trompeur. J'ai ajouté un Assert après l'arrangement. Cela a fait échouer le test dans un endroit mettant en évidence le problème.

Je pense qu'il y a aussi une odeur de code ici si la partie Arrangement du test est trop longue et compliquée.

1
WW.

Je le fais maintenant. A-A-A-A d'un genre différent

Arrange - setup
Act - what is being tested
Assemble - what is optionally needed to perform the assert
Assert - the actual assertions

Exemple de test de mise à jour:

Arrange: 
    New object as NewObject
    Set properties of NewObject
    Save the NewObject
    Read the object as ReadObject

Act: 
    Change the ReadObject
    Save the ReadObject

Assemble: 
    Read the object as ReadUpdated

Assert: 
    Compare ReadUpdated with ReadObject properties

La raison en est que l'ACT ne contient pas la lecture de ReadUpdated, car il ne fait pas partie de l'acte. L'acte ne fait que changer et sauver. Tellement vraiment, ARRANGE ReadUpdated pour l'assertion, j'appelle ASSEMBLE pour l'assertion. Ceci est pour éviter de confondre la section ARRANGE

ASSERT ne doit contenir que des assertions. Cela laisse ASSEMBLE entre ACT et ASSERT qui établit l’affirmation.

Enfin, si vous échouez dans Arrange, vos tests ne sont pas corrects car vous devriez avoir d’autres tests pour empêcher/trouver ces trivial bugs. Parce que pour le scénario que je présente, il devrait déjà y avoir d’autres tests qui testent READ et CREATE. Si vous créez une "Assertion de garde", vous risquez de casser DRY et de créer une maintenance.

1
Valamas

Si vous voulez vraiment tout tester dans l'exemple, essayez plus de tests ... comme:

public void testIncludes7() throws Exception {
    Range range = new Range(0, 5);
    assertFalse(range.includes(7));
}

public void testIncludes5() throws Exception {
    Range range = new Range(0, 5);
    assertTrue(range.includes(5));
}

public void testIncludes0() throws Exception {
    Range range = new Range(0, 5);
    assertTrue(range.includes(0));
}

public void testEncompassInc7() throws Exception {
    Range range = new Range(0, 5);
    range.encompass(7);
    assertTrue(range.includes(7));
}

public void testEncompassInc5() throws Exception {
    Range range = new Range(0, 5);
    range.encompass(7);
    assertTrue(range.includes(5));
}

public void testEncompassInc0() throws Exception {
    Range range = new Range(0, 5);
    range.encompass(7);
    assertTrue(range.includes(0));
}

Parce que sinon, il vous manque tant de possibilités d'erreur ... par exemple après englob, la plage n'inclut que 7, etc ... Il existe également des tests de longueur de plage (pour vous assurer qu'elle ne comprend pas également une valeur aléatoire ), et un autre ensemble de tests entièrement pour tenter d’englober 5 dans la plage ... à quoi s’attendrait-on - une exception dans englob, ou la plage doit-elle être modifiée?

Quoi qu'il en soit, le fait est que si vous souhaitez tester certaines hypothèses de la loi, placez-les dans leur propre test, oui?

0
Andrew

Je n'utilise pas ce modèle, parce que je pense faire quelque chose comme:

Arrange
Assert-Not
Act
Assert

Peut-être inutile, car vous savez supposément que votre pièce Arrange fonctionne correctement, ce qui signifie que tout ce qui se trouve dans la pièce Arrangement doit être testé également ou doit être assez simple pour ne pas en avoir besoin.

En utilisant l'exemple de votre réponse:

public void testEncompass() throws Exception {
    Range range = new Range(0, 5);
    assertFalse(range.includes(7)); // <-- Pointless and against DRY if there 
                                    // are unit tests for Range(int, int)
    range.encompass(7);
    assertTrue(range.includes(7));
}
0

Dépend de votre environnement/langue de test, mais généralement si quelque chose dans la partie Arrangement échoue, une exception est générée et le test ne s'affiche pas au lieu de démarrer la partie Act. Donc non, d'habitude je n'utilise pas une deuxième partie Assert.

De plus, dans le cas où votre partie Arrangement est assez complexe et ne lève pas toujours une exception, vous pourriez peut-être envisager de l'envelopper dans une méthode et d'écrire son propre test, de sorte que vous puissiez être sûr qu'elle n'échouera pas (sans lancer une exception).

0
schnaader

J'utilise:

1. Setup
2. Act
3. Assert 
4. Teardown

Parce qu'une installation propre est très importante.

0
kame