-
Notifications
You must be signed in to change notification settings - Fork 5
/
thucydides-fr.doc
3975 lines (2924 loc) · 186 KB
/
thucydides-fr.doc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
= Thucydides: manuel de référence
John Ferguson Smart
v0.9.125, septembre 2013
:author: John Ferguson Smart
:data-uri:
:doctype: book
:toc:
:lang: fr
:encoding: utf-8
[[preface-copyright]]
[preface]
== Copyright
Copyright © 2011-2013 John Ferguson Smart.
Version en ligne publiée par Wakaleo Consulting.
Ce travail est sous licence Creative Commons Attribution - Pas d'utilisation
Commerciale - Pas de Modification 3.0 United States Pour plus d'information sur
cette licence, voir
http://creativecommons.org/licenses/by-nc-nd/3.0/fr/[creativecommons.org/licenses/by-nc-nd/3.0/fr/].
Java™ et toutes les marques et logos basés sur Java sont des marques
commerciales ou marques commerciales enregistrées de Sun Microsystems, Inc.,
aux États-Unis et autres pays.
Éclipse™ est une marque commerciale de Eclipse Foundation, Inc., aux États-Unis
et autres pays.
Apache et le logo plume Apache sont des marques commerciales de la Apache
Software Foundation.
Plusieurs des appellations utilisées par les fabricants et les vendeurs pour
désigner leurs produits sont revendiqués comme marques commerciales. Quand ces
appellations apparaissent dans ce livre, et quand Wakaleo Consulting était au
fait de cette revendication de marque commerciale, les appellations ont été
affichées en majuscules ou avec des initiales en majuscule.
Bien que toutes les précautions ont été prises dans la préparation de ce livre,
l'éditeur et les auteurs déclinent toute responsabilité pour les erreurs ou
omissions, ou pour les dommages résultant de l'utilisation des informations
fournies ci-après.
[[introduction]]
== Introduction à Thucydides
Thucydides (Tou-saille-didz) est un outil conçu pour faciliter l'écriture des tests de recette et de non régression automatisés. Il offre des fonctionnalités pour faciliter l'organisation et la structuration de vos tests de recette en les reliant avec vos histoires utilisateur ou les fonctionnalités qu'ils testent. Quand les tests sont exécutés, Thucydides génère une documentation illustrée qui décrit comment l'application est utilisée en s'appuyant sur les histoires décrites par les tests.
Thucydides apporte une prise en charge robuste des tests web automatisés utilisant Selenium 2, bien qu'il puisse être utilisé efficacement pour des tests non web.
Thucydides était un historien grec connu pour ses analyses astucieuses et qui consignait rigoureusement les événements dont il était témoin ou auxquels il participait lui-même. De la même manière, le framework Thucydides observe et analyse vos tests de recette et consigne de façon détaillée leur exécution.
[[basic-concepts]]
== Concepts fondamentaux du test de recette et de non régression
Pour tirer le meilleur parti de Thucydides, il est utile de comprendre quelques principes de base sous-jacents au développement dirigé par les tests de recette (Acceptance Test Driven Development). Thucydides est communément utilisé à la fois pour les tests de recette automatisés et les tests de non régression et les principes abordés ici s'appliquent aux deux, à quelques variations mineures près.
Acceptance Test Driven Development, ou ATDD, est une forme avancée du développement dirigé par les tests (Test Driven Development ou TDD) dans lequel les critères de recette automatisées - définis en collaboration avec les utilisateurs - pilotent et orientent le processus de développement. Ceci aide à s'assurer que tout le monde comprend quelles sont les fonctionnalités en cours de développement.
Un des points importants au sujet de l'ATDD est l'idée de la "Spécification par l'exemple". Spécification par l'exemple fait référence à l'utilisation d'exemples relativement concrets pour illustrer comment un système doit fonctionner, par opposition à des spécifications écrites plus formellement.
Prenons un exemple. Dans de nombreux projets, les exigences sont exprimées à l'aide de simples histoires telles que ce qui suit:
...................................
De façon à obtenir de l'argent pour acheter une nouvelle voiture
En tant que propriétaire d'une voiture
Je veux vendre ma vieille voiture sur Internet
...................................
Si nous développons un site web de vente de voitures qui aide les gens à atteindre cet objectif, nous devrions typiquement définir une série de critères de recette pour donner corps à notre histoire. Par exemple, nous pourrions avoir les critères suivants dans notre liste de critères de recette:
- Le propriétaire de voiture peut publier une annonce de vente en ligne standard
- Le propriétaire de voiture peut publier une annonce de vente en ligne premium
- L'annonce voiture peut afficher la marque, le modèle et l'année de la voiture
et ainsi de suite.
Un testeur planifiant les tests pour ces critères de recette peut concevoir un plan de tests décrivant la façon dont il entend tester ces critères de recette. Pour le premier critère, il peut commencer par un plan de haut niveau tel que ce qui suit:
* Aller à la section des annonces voiture et choisir de poster une annonce voiture standard
* Saisir les informations relatives à la voiture
* Choisir les options de publication
* Pré visualiser l'annonce
* Saisir les informations de paiement
* Visualiser la confirmation de l'annonce
Chacune de ces étapes peut nécessiter d'être décomposée en étapes plus fines.
* 'Saisir les informations relatives à la voiture'
** Saisir le fabriquant, le modèle et l'année de la voiture
** Choisir les options
** Ajouter des photos
** Saisir une description
Ces étapes sont souvent étoffées avec plus de détails concrets:
* 'Saisir les informations relatives à la voiture'
** Créer une annonce pour une Mitsubishi Pajero de 2006
** Ajouter Air conditionné et lecteur CD
** Ajouter trois photos
** Saisir une description
Selon nos objectifs, les tests de non régression peuvent être définis comme des tests de bout en bout qui assurent que l'application se comporte comme attendu et qui continuera à se comporter comme attendu dans les prochaines livraisons. Autant les tests de recette sont définis très tôt dans le processus, avant le début du développement, autant les tests de non régression implique l'existence du système. A par cela, les étapes nécessaires à la définition et à l'automatisation des tests sont très semblables.
Maintenant différentes parties prenantes du projet seront impliquées à différents niveaux de détail. Certains, tels que les gestionnaires de projet et les gestionnaires en général, ne seront intéressés que par les fonctionnalités qui fonctionnent et par celles qui doivent être réalisées. D'autres, tels que les analystes métier et les qualiticiens, seront intéressés par la façon détaillée dont les scénarios de recette sont implémentés, éventuellement en allant jusqu'aux écrans.
Thucydides vous aide à structurer vos tests de recette automatisés en étapes et sous-étapes comme illustré ci-dessous. Ceci a tendance à rendre les tests plus clairs, plus flexibles et plus faciles à maintenir. De plus, quand les tests sont exécutés, Thucydides produit des rapports illustrés en style narratif comme dans <<fig-test-report>>.
[[fig-test-report]]
.Un rapport de test généré par Thucydides
image::figs/test-report.png[scaledwidth="80%", width=475]
Quand vient le moment d'implémenter les tests eux-mêmes, Thucydides apporte également de nombreuses fonctionnalités pour rendre ceci plus facile, plus rapide et plus propre de façon à écrire des tests clairs et maintenables. Ceci est particulièrement vrai pour les tests web automatisés utilisant Selenium 2, mais Thucydides répond aussi bien aux besoins des tests non-web. Thucydides fonctionne actuellement avec JUnit et easyb - l'intégration avec d'autres framework BDD est en cours.
== Débuter avec Thucydides
=== Créer un nouveau projet Thucydides
La façon la plus simple de démarrer un nouveau projet Thucydides est d'utiliser l'archétype Maven. Trois archétypes sont actuellement disponibles: un pour utiliser Thucydides avec JUnit, un autre si vous voulez également écrire vos tests de recette (ou une partie d'entre eux) en utilisant http://www.easyb.org/[easyb] et enfin un supplémentaire pour vous aider à écrire vos tests de recette avec jBehave. Dans cette section, nous créerons un nouveau projet Thucydides en utilisant l'archétype Thucydides, puis nous aborderons les fonctionnalités essentielles de ce projet.
En ligne de commandes, vous pouvez exécuter mvn archetype:generate puis choisir l'archétype net.thucydides.thucydides-easyb-archetype dans la liste des archétypes proposés. Ou bien, vous pouvez utiliser votre IDE préféré pour générer un nouveau projet Maven en utilisant un archétype.
--------------------------
$ mvn archetype:generate
...
Define value for property 'groupId': : com.mycompany
Define value for property 'artifactId': : webtests
Define value for property 'version': 1.0-SNAPSHOT: :
Define value for property 'package': com.mycompany: :
Confirm properties configuration:
groupId: com.mycompany
artifactId: webtests
version: 1.0-SNAPSHOT
package: com.mycompany
Y: :
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 2:33.290s
[INFO] Finished at: Fri Oct 28 07:20:41 NZDT 2011
[INFO] Final Memory: 7M/81M
[INFO] ------------------------------------------------------------------------
--------------------------
Ceci va créer un projet Thucydides simple complété d'un objet page, d'une bibliothèque d'étapes et de deux cas de tests, l'un utilisant JUnit et l'autre easyb. Les tests effectifs sont exécutés sur le dictionnaire en ligne Wiktionary.org. Avant d'aller plus loin, faisons un tour dans notre projet. Mais avant, nous devons ajouter +net.thucydides.maven.plugins+ à nos groupes de plugin dans notre fichier +settings.xml+:
---------------------------
<settings>
<pluginGroups>
<pluginGroup>net.thucydides.maven.plugins</pluginGroup>
...
</pluginGroups>
...
</settings>
---------------------------
Ceci va nous permettre d'appeler le plugin Maven Thucydides à partir de la ligne de commandes sous la forme abrégée montrée ici. Maintenant, allons dans le répertoire généré pour notre projet, lançons les tests et générons le rapport:
---------------------------
$ mvn test thucydides:aggregate
---------------------------
Ceci doit lancer des tests web et générer un rapport dans le répertoire +target/site/thucydides+ (ouvrez le fichier +index.html+).
Si vous descendez dans les rapports de tests individuels, vous verrez un récit illustré pour chaque test comme celui montré ici <<fig-test-report>>
Maintenant les détails. La structure projet est montrée ici:
--------------------------
+ src
+ main
+ java
+ com.mycompany.pages
- HomePage.java
+ test
+ java
+ com.mycompany.pages
+ requirements
- Application.java
+ steps
- EndUserSteps.java
- LookupADefinitionStoryTest.java
+ stories
+ com.mycompany
- LookupADefinition.story
--------------------------
Ce projet est conçu pour fournir un point de départ à vos tests de recette Thucydides et pour illustrer certaines des fonctionnalités de base. Les tests sont fournis dans deux styles: 'easyb' et 'JUnit'. easyb est une bibliothèque BDD (Behaviour Driven Development) reposant sur Groovy qui fonctionne bien pour ce type de tests. L'histoire exemple easyb se trouve dans le fichier +LookupADefinition.story+ et ressemble à quelque chose comme ça:
--------------------------
using "thucydides"
thucydides.uses_default_base_url "http://en.wiktionary.org/wiki/Wiktionary:Main_Page"
thucydides.uses_steps_from EndUserSteps
thucydides.tests_story SearchByKeyword
scenario "Looking up the definition of 'apple'", {
given "the user is on the Wikionary home page", {
end_user.is_the_home_page()
}
when "the end user looks up the definition of the word 'apple'", {
end_user.looks_for "apple"
}
then "they should see the definition of 'apple", {
end_user.should_see_definition_containing_words "A common, round fruit"
}
}
--------------------------
Un coup d'oeil rapide à cette histoire montre qu'elle se rapporte à un utilisateur cherchant la définition du mot 'apple'. Seul le "quoi" est exprimé à ce niveau - les détails sont masqués dans les étapes du test, voire plus profondément, dans les objets page.
Si vous préférez des tests en pur Java, l'équivalent JUnit se trouve dans le fichier +LookupADefinitionStoryTest.java+:
--------------------------
@Story(Application.Search.SearchByKeyword.class)
@RunWith(ThucydidesRunner.class)
public class LookupADefinitionStoryTest {
@Managed(uniqueSession = true)
public WebDriver webdriver;
@ManagedPages(defaultUrl = "http://en.wiktionary.org/wiki/Wiktionary:Main_Page")
public Pages pages;
@Steps
public EndUserSteps endUser;
@Issue("#WIKI-1")
@Test
public void looking_up_the_definition_of_apple_should_display_the_corresponding_article() {
endUser.is_the_home_page();
endUser.looks_for("apple");
endUser.should_see_definition_containing_words("A common, round fruit");
}
}
--------------------------
Comme vous pouvez le voir, c'est un petit peu plus technique mais cela reste à très haut niveau.
Les bibliothèques d'étapes contiennent l'implémentation de chacune des étapes utilisées dans les tests de haut niveau. Pour des tests complexes, ces étapes peuvent à leur tour faire appel à d'autres étapes. La bibliothèque d'étapes utilisée dans cet exemple peut être trouvée dans +EndUserSteps.java+:
--------------------------
public class EndUserSteps extends ScenarioSteps {
public EndUserSteps(Pages pages) {
super(pages);
}
@Step
public void searches_by_keyword(String keyword) {
enters(keyword);
performs_search();
}
@Step
public void enters(String keyword) {
onHomePage().enter_keywords(keyword);
}
@Step
public void performs_search() {
onHomePage().starts_search();
}
private HomePage onHomePage() {
return getPages().currentPageAt(HomePage.class);
}
@Step
public void should_see_article_with_title(String title) {
assertThat(onHomePage().getTitle(), is(title));
}
@Step
public void is_on_the_wikipedia_home_page() {
onHomePage().open();
}
}
--------------------------
Les objets Page sont un moyen d'encapsuler les détails d'implémentation d'une page donnée. Selenium 2 possède un particulièrement bon support pour les objets page et ThucydidesRunner en tire parti. L'objet page d'exemple se trouve dans la classe HomePage.java:
--------------------------
@DefaultUrl("http://en.wiktionary.org/wiki/Wiktionary:Main_Page")
public class SearchPage extends PageObject {
@FindBy(name="search")
private WebElement searchInput;
@FindBy(name="go")
private WebElement searchButton;
public SearchPage(WebDriver driver) {
super(driver);
}
public void enter_keywords(String keyword) {
searchInput.sendKeys(keyword);
}
public void starts_search() {
searchButton.click();
}
public List<String> getDefinitions() {
WebElement definitionList = getDriver().findElement(By.tagName("ol"));
List<WebElement> results = definitionList.findElements(By.tagName("li"));
return convert(results, new ExtractDefinition());
}
class ExtractDefinition implements Converter<WebElement, String> {
public String convert(WebElement from) {
return from.getText();
}
}
}
--------------------------
La pièce finale du puzzle est la classe +Application.java+ qui est un moyen de représenter la structure de vos exigences sous forme Java de telle sorte que vos tests easyb et JUnit puissent être reliés aux exigences qu'ils testent:
--------------------------
public class Application {
@Feature
public class Search {
public class SearchByKeyword {}
public class SearchByAnimalRelatedKeyword {}
public class SearchByFoodRelatedKeyword {}
public class SearchByMultipleKeywords {}
public class SearchForQuote{}
}
@Feature
public class Backend {
public class ProcessSales {}
public class ProcessSubscriptions {}
}
@Feature
public class Contribute {
public class AddNewArticle {}
public class EditExistingArticle {}
}
}
--------------------------
C'est ce qui permet à Thucydides de générer un rapport global concernant les fonctionnalités et les histoires.
Dans les sections suivantes, nous verrons plus en détails les différents aspects de l'écriture des tests automatiques avec Thucydides.
=== Configurer des capacités personnalisées pour le pilote web
Vous pouvez configurer des capacités personnalisées pour le pilote web en passant une liste de capacités séparées par des points virgules dans la propriété +thucydides.driver.capabilities+. Par exemple,
--------------------------
"build:build-1234; max-duration:300; single-window:true; tags:[étiquette1,étiquette2,étiquette3]"
--------------------------
== Écrire des tests de recette avec Thucydides
Dans cette section, nous examinons plus en détail les choses dont nous avons besoin pour écrire nos tests de recette ou de non régression en utilisant Thucydides. Nous allons également présenter une approche générale pour écrire des tests de recette web qui a bien fonctionné pour nous par le passé.
. Définir et organiser les exigences ou les histoires utilisateur que vous avez besoin de tester
. Écrire des tests haut niveau en attente pour les critères de recette
. Choisir un test à implémenter et le diviser en petites étapes de haut niveau (typiquement entre 3 et 7)
. Implémenter ces étapes, soit en les divisant en d'autres étapes, soit en accédant à des objets page.
. Implémenter toutes les méthodes d'objet Page que vous avez découvertes.
[NOTE]
=====================================================================
Ces étapes ne doivent pas être vues comme une approche linéaire ou en cascade. En effet, le processus est habituellement assez incrémental avec des exigences ajoutées à la classe Application quand elles sont nécessaires, et des tests en attente utilisés pour définir les tests avant qu'ils ne soient complétés.
=====================================================================
=== Organiser vos exigences
Pour tirer le meilleur de vos tests automatisés dans Thucydides, vous devez dire à Thucydides quelles fonctionnalités de votre application vous testez dans chaque test. Bien que cette étape soit facultative, elle est fortement recommandée.
La version actuelle de Thucydides utilise une organisation simple à trois niveaux pour structurer les tests de recette en morceaux plus facilement gérables. Au plus haut niveau, une application est divisée en 'fonctionnalités' ('feature'), qui est une fonction de haut niveau ou un groupe de fonctions en rapport. Une fonctionnalité contient un certain nombre d'histoires ('stories', correspondant aux histoires utilisateur, cas d'utilisation, etc.). Chaque histoire est validée par un certain nombre d'exemples, ou critères d'acceptation, qui sont automatisés sous forme de tests web (parfois appelés scénarios). Chaque test, à son tour, est implémenté en utilisant un certain nombre d'étapes.
Bien sûr cette structure et ces termes sont principalement des commodités pour permettre une vision haut niveau de vos tests de recette. Cependant, ce type d'abstraction à 3 niveaux semble être assez commune.
Dans la version actuelle de Thucydides, vous définissez cette structure à l'intérieur du code de test sous forme de classes Java (très légères) footnote:[Les futures versions de Thucydides offriront d'autres façons de définir vos exigences utilisateur.]. Ceci facilite la refactorisation et le renommage des histoires utilisateur et des fonctionnalités à l'intérieur des tests, et donne un point de référence central dans la suite de tests illustrant quelles fonctionnalités sont en cours de test. Un exemple simple est donné ici. La classe Application est simplement un moyen pratique de regrouper dans un seul fichier les fonctionnalités et les histoires utilisateur. Les fonctionnalités sont repérées avec l'annotation @Feature. Les histoires utilisateur sont déclarées sous forme de classes internes à l'intérieur d'une classe @Feature.
--------------------------
public class Application {
@Feature
public class ManageCompanies {
public class AddNewCompany {}
public class DeleteCompany {}
public class ListCompanies {}
}
@Feature
public class ManageCategories {
public class AddNewCategory {}
public class ListCategories {}
public class DeleteCategory {}
}
@Feature
public class ManageTags {
public class DisplayTagCloud {}
}
@Feature
public class ManageJobs {}
@Feature
public class BrowseJobs {
public class UserLookForJobs {}
public class UserBrowsesJobTabs {}
}
}
--------------------------
== Définir des tests de haut niveau
Il y a deux approches pour automatiser les tests de recette ou de non régression avec Thucydides. Les deux impliquent d'implémenter les tests sous forme d'une séquence d'étapes de très haut niveau puis d'implémenter ces étapes en descendant dans les détails jusqu'à atteindre les objets page. La différence réside dans le langage utilisé pour implémenter les tests de haut niveau. Des outils tels que easyb se concentrent davantage sur la communication avec les non-développeurs et permettent aux tests de haut niveau d'être exprimés plus facilement en termes métier. D'un autre côté, les développeurs trouvent souvent plus confortable de travailler directement avec JUnit, donc, si la communication avec des parties prenantes non techniciens n'est pas une priorité, cette façon de faire peut être envisagée favorablement.
Dans la version actuelle de Thucydides, vous pouvez écrire vos tests en utilisant easyb (pour une approche davantage dans le style BDD) ou en JUnit en utilisant Java ou un autre langage JVM (Groovy est un choix populaire). D'autres outils BDD seront gérés dans de futures versions. Nous aborderons les deux ici, mais vous pouvez utiliser celle avec laquelle vous et votre équipe êtes le plus à l'aise.
=== Définir des tests haut niveau en easyb
Easyb (http://easyb.org) est un outil BDD basé sur Groovy. Il facilite l'écriture d'histoires et de scénarios légers en utilisant la structuration classique du style BDD "given-when-then" ("étant donné-lorsque-alors") et en les implémentant en Groovy. Le plugin easyb de Thucydides est conçu pour faciliter l'écriture des tests Thucydides en utilisant easyb.
==== Écrire une histoire easyb en attente
Avec easyb, vous écrivez des tests (appelés "scenarios") qui, lorsqu'on utilise Thucydides, correspondent aux critères de recette automatisée. Les tests sont groupés en "histoires" ou "stories" - chaque histoire possède son propre fichier.
Les scénarios sont d'abord écrits comme "pending" (en attente). Ce sont juste des aperçus haut niveau, décrivant un ensemble de critères d'acceptation pour une histoire donnée et selon la structuration "given-when-then".
Quand les tests sont exécutés, les scénarios en attente sont ignorés. Cependant, ils apparaissent dans les rapports, de telle sorte que vous savez quelles fonctionnalités doivent encore être implémentées. Un exemple de la façon dont les scénarios en attente apparaissent dans un rapport Thucydides figure dans <<fig-story-results-pending>>.
[[fig-story-results-pending]]
.Les tests en attente sont affichés avec l'icône 'calendrier'
image::figs/story-results-pending.png[scaledwidth="80%", width=475]
Voici un exemple d'histoire easyb en attente utilisant Thucydides:
----------------------
using "thucydides"
import net.thucydides.demos.jobboard.requirements.Application.ManageCategories.AddNewCategory
thucydides.tests_story AddNewCategory
scenario "The administrator adds a new category to the system",
{
given "a new category needs to be added to the system"
when "the administrator adds a new category"
then "the system should confirm that the category has been created"
and "the new category should be visible to job seekers"
}
scenario "The administrator adds a category with an existing code to the system",
{
given "the administrator is on the categories list page"
when "the user adds a new category with an existing code"
then "an error message should be displayed"
}
----------------------
Examinons cette histoire partie par partie. D'abord, vous devez déclarer que vous utilisez Thucydides. Vous faites cela en utilisant le mot clef easyb:
----------------------
using "thucydides"
----------------------
Ceci va, entre autres choses, injecter l'objet +thucydides+ dans le contexte de votre histoire de telle sorte que vous pouvez configurer Thucydides pour exécuter votre histoire correctement.
Ensuite, vous devez dire à Thucydides quelle histoire vous être en train de tester. Vous faites cela en référençant l'une des classes d'histoire définies précédemment. C'est ce que nous faisons ici:
--------------------------
import net.thucydides.demos.jobboard.requirements.Application.ManageCategories.AddNewCategory
thucydides.tests_story AddNewCategory
--------------------------
Le reste de l'histoire easyb est juste une série de scénarios en attente easyb habituels. Pour le moment, il n'y a aucune implémentation de telle sorte qu'ils apparaissent en attente ("pending") dans les rapports:
----------------------
scenario "The administrator adds a new category to the system",
{
given "a new category needs to be added to the system"
when "the administrator adds a new category"
then "the system should confirm that the category has been created"
and "the new category should be visible to job seekers"
}
scenario "The administrator adds a category with an existing code to the system",
{
given "the administrator is on the categories list page"
when "the user adds a new category with an existing code"
then "an error message should be displayed"
}
----------------------
Typiquement, vous déclarez de nombreuses histoires en attente, de préférence en collaboration avec le propriétaire du produit ou les analystes métier au début d'une itération. Ceci vous donne une bonne idée de quelles histoires doivent être implémentées dans une itération donnée ainsi qu'une idée de la complexité relative de chaque histoire.
==== Implémenter des histoires easyb
L'étape suivante consiste à implémenter vos histoires. Voyons une version implémentée du premier de ces scénarios:
----------------------
using "thucydides"
import net.thucydides.demos.jobboard.requirements.Application.ManageCategories.AddNewCategory
import net.thucydides.demos.jobboard.steps.AdministratorSteps
import net.thucydides.demos.jobboard.steps.JobSeekerSteps
thucydides.uses_default_base_url "http://localhost:9000"
thucydides.uses_steps_from AdministratorSteps
thucydides.uses_steps_from JobSeekerSteps
thucydides.tests_story AddNewCategory
def cleanup_database() {
administrator.deletes_category("Scala Developers");
}
scenario "The administrator adds a new category to the system",
{
given "a new category needs to be added to the system",
{
administrator.logs_in_to_admin_page_if_first_time()
administrator.opens_categories_list()
}
when "the administrator adds a new category",
{
administrator.selects_add_category()
administrator.adds_new_category("Scala Developers","SCALA")
}
then "the system should confirm that the category has been created",
{
administrator.should_see_confirmation_message "The Category has been created"
}
and "the new category should be visible to job seekers",
{
job_seeker.opens_jobs_page()
job_seeker.should_see_job_category "Scala Developers"
}
}
----------------------
De nouveau, décomposons ceci. Dans la première partie, nous importons les classes que nous avons besoin d'utiliser:
----------------------
using "thucydides"
import net.thucydides.demos.jobboard.requirements.Application.ManageCategories.AddNewCategory
import net.thucydides.demos.jobboard.steps.AdministratorSteps
import net.thucydides.demos.jobboard.steps.JobSeekerSteps
----------------------
Ensuite, nous déclarons l'URL de base par défaut à utiliser pour les tests. Comme l'annotation équivalente dans les tests JUnit, celle-ci est utilisée pour les tests exécutés depuis l'IDE ou si aucune URL de base n'est définie en ligne de commande en utilisant le paramètre +webdriver.base.url+.
---------------------
thucydides.uses_default_base_url "http://localhost:9000"
---------------------
Nous avons également besoin de déclarer les bibliothèques d'étapes de test que nous allons utiliser. Nous le faisons en utilisant thucydides.uses_steps_from. Ceci va injecter une variable d'instance dans le contexte easyb pour chaque bibliothèque d'étape déclarée. Si le nom de classe de la bibliothèque d'étape finit en 'Steps' (par exemple JobSeekerSteps), le nom de la variable sera le nom de la classe moins le suffixe 'Steps', converti en minuscules et sous-ligné (ex. "job_seeker"). Nous en apprendrons davantage sur l'implémentation des bibliothèques d'étapes plus loin.
---------------------
thucydides.uses_steps_from AdministratorSteps
thucydides.uses_steps_from JobSeekerSteps
thucydides.tests_story AddNewCategory
---------------------
Enfin, nous implémentons le scénario. Notez que, puisqu'il s'agit de Groovy, nous pouvons déclarer des méthodes d'outillage pour faciliter la préparation et le nettoyage de l'environnement de test selon les besoins:
---------------------
def cleanup_database() {
administrator.deletes_category("Scala Developers");
}
---------------------
Habituellement, l'implémentation invoque seulement des méthodes d'étape, comme illustré ici:
---------------------
scenario "The administrator adds a new category to the system",
{
given "a new category needs to be added to the system",
{
administrator.logs_in_to_admin_page_if_first_time()
administrator.opens_categories_list()
}
when "the administrator adds a new category",
{
administrator.selects_add_category()
administrator.adds_new_category("Scala Developers","SCALA")
}
then "the system should confirm that the category has been created",
{
administrator.should_see_confirmation_message "The Category has been created"
}
and "the new category should be visible to job seekers",
{
job_seeker.opens_jobs_page()
job_seeker.should_see_job_category "Scala Developers"
cleanup_database()
}
}
---------------------
=== Définir de tests de haut niveau avec JUnit
Thucydides s'intègre facilement avec les tests JUnit 4 ordinaires en utilisant le lanceur de tests ThucydidesRunner et quelques annotations spécialisées. C'est une des manières les plus simples pour commencer avec Thucydides, et cela est très bien adapté aux tests de non régression où la communication et les clarifications avec les différentes parties prenantes n'est pas une exigence.
Voici un exemple de test web JUnit Thucydides:
-------------------
@RunWith(ThucydidesRunner.class)
@Story(UserLookForJobs.class)
public class LookForJobsStory {
@Managed
public WebDriver webdriver;
@ManagedPages(defaultUrl = "http://localhost:9000")
public Pages pages;
@Steps
public JobSeekerSteps job_seeker;
@Test
public void user_looks_for_jobs_by_key_word() {
job_seeker.opens_jobs_page();
job_seeker.searches_for_jobs_using("Java");
job_seeker.should_see_message("No jobs found.");
}
@Test
public void when_no_matching_job_found_should_display_error_message() {
job_seeker.opens_jobs_page();
job_seeker.searches_for_jobs_using("unknownJobCriteria");
job_seeker.should_see_message("No jobs found.");
}
@Pending @Test
public void tags_should_be_displayed_to_help_the_user_find_jobs() {}
@Pending @Test
public void the_user_can_list_all_of_the_jobs_for_a_given_tag() {}
@Pending @Test
public void the_user_can_see_the_total_number_of_jobs_on_offer() {}
}
-------------------
Examinons ceci section par section. La classe commence par l'annotation @RunWith pour indiquer qu'il s'agit d'un test Thucydides. Nous utilisons également l'annotation @Story pour indiquer quelle histoire utilisateur est testée (définie comme classe imbriquée des classes @Feature vues plus haut). Ceci est utilisé pour générer des rapports globaux.
-------------------
@RunWith(ThucydidesRunner.class)
@Story(UserLookForJobs.class)
public class LookForJobsStory {
...
-------------------
Ensuite arrivent deux annotations essentielles pour tous les tests web. Tout d'abord, votre cas de test a besoin d'un champ public +Webdriver+ annoté avec +@Managed+. Ceci permet à Thucydides de prendre soin pour vous de l'ouverture et de la fermeture du pilote WebDriver et d'utiliser ce pilote dans les pages et les étapes de test quand les tests sont exécutés:
-------------------
@Managed
public WebDriver webdriver;
-------------------
Le second champ essentiel est une instance de la classe +Pages+, annotée avec +@ManagedPages+. Il s'agit essentiellement d'une fabrique de page que Thucydides utilise pour vous fournir des objets page instanciés. L'attribut +defaultUrl+ vous permet de définir une URL à utiliser à l'ouverture de vos pages si aucune autre URL de base n'a été définie. Ceci est utile pour les tests dans l'IDE:
-------------------
@ManagedPages(defaultUrl = "http://localhost:9000")
public Pages pages;
-------------------
Notez que ces annotations ne sont nécessaires que pour les tests web. Si votre test Thucydides n'utilise pas de tests web, vous pouvez les ignorer en toute sérénité.
Pour des tests haut niveau de recette ou de non régression, c'est une bonne habitude de définir les tests de haut niveau comme une suite d'étapes de haut niveau. Cela rendra vos tests plus lisibles et plus faciles à maintenir si vous déléguez les détails d'implémentation de votre test (le "comment") à des méthodes réutilisables d'étape. Nous verrons comment définir ces méthodes d'étape plus tard. Cependant, vous devez au minimum définir la classe dans laquelle les étapes seront définies, en utilisant l'annotation @Steps. Cette annotation demande à Thucydides d'être à l'écoute des appels de méthodes de cet objet et (pour les tests web) d'injecter l'instance WebDriver et la fabrique de page dans la classe Steps de telle sorte qu'elles puissent être utilisées dans les méthodes d'étapes.
-------------------
@Steps
public JobSeekerSteps job_seeker;
-------------------
==== Tests en attente
Les tests qui ne contiennent aucune étape sont considérés comme étant en attente ('pending'). Vous pouvez également forcer une étape à être ignorée (et signalée comme étant en attente) en utilisant l'annotation +@Pending+ ou l'annotation +@Ignore+. Noter que le sens est légèrement différent: +@Ignore+ indique que vous avez temporairement suspendu l'exécution d'un test tandis que +@Pending+ signifie que le test a été spécifié mais pas encore implémenté. Ainsi, les deux tests suivants seront en attente:
-------------------
@Test
public void administrator_adds_an_existing_company_to_the_system() {}
@Pending @Test
public void administrator_adds_a_company_with_an_existing_code_to_the_system() {
steps.login_to_admin_page();
steps.open_companies_list();
steps.select_add_company();
// More to come
}
-------------------
Un test sera également considéré comme étant en attente si au moins l'une des étapes utilisées dans ce test est en attente. Pour qu'une étape soit en attente, elle doit être annotée avec +@Pending+.
===== hypothèses JUnit ( assumptions )
Vous pouvez utiliser les http://junit.sourceforge.net/javadoc/org/junit/Assume.html[hypothèses] JUnit dans vos tests ou vos méthodes d'étapes. Les étapes pour lesquelles les conditions sous hypothèse échouent sont marquées EN SUSPEND au lieu de ERREUR. Les étapes suivantes sont également marquées comme étant EN SUSPEND.
-------------------
@Test
public void administrator_adds_an_existing_company_to_the_system() {}
@Test
public void administrator_adds_a_company_with_an_existing_code_to_the_system() {
steps.login_to_admin_page();
steps.open_companies_list();
steps.select_add_company();
// More to come
}
-------------------
-------------------
...
@Step
public void open_companies_list() {
Assume.assumeThat(user.role, is("admin"));
CompaniesListPage page = pages().get(CompanyListPage.class);
String companieslist = page.getCompaniesList();
}
...
-------------------
Dans l'exemple suivant, si l'hypothèse dans l'étape +open_companies_list+ échoue, celle-ci et toutes les étapes suivantes seront marquées comme EN SUSPEND.
==== Exécuter des tests dans une unique session du navigateur
Normalement, Thucydides ouvre une session du navigateur pour chaque test. Ceci permet de s'assurer plus facilement que chaque test est isolé et indépendant. Cependant, parfois, il est utile d'être capable d'exécuter des tests dans une même session du navigateur, en particulier pour des raisons de performance sur des écrans en lecture seule. Vous pouvez faire cela en utilisant l'attribut 'uniqueSession' de l'annotation @Managed comme montré ci-dessous. Dans ce cas, le navigateur va s'ouvrir au début du cas de test et ne se fermera pas tant que tous les tests n'auront pas été exécutés.
----------------
@RunWith(ThucydidesRunner.class)
public class OpenStaticDemoPageSample {
@Managed(uniqueSession=true)
public WebDriver webdriver;
@ManagedPages(defaultUrl = "classpath:static-site/index.html")
public Pages pages;
@Steps
public DemoSiteSteps steps;
@Test
@Title("The user opens the index page")
public void the_user_opens_the_page() {
steps.should_display("A visible title");
}
@Test
@Title("The user selects a value")
public void the_user_selects_a_value() {
steps.enter_values("Label 2", true);
steps.should_have_selected_value("2");
}
@Test
@Title("The user enters different values.")
public void the_user_opens_another_page() {
steps.enter_values("Label 3", true);
steps.do_something();
steps.should_have_selected_value("3");
}
}
----------------
Si vous n'avez pas besoin de la gestion de WebDriver dans vos tests, vous pouvez omettre les annotations +@Managed+ et +@Pages+, par exemple:
----------------
@RunWith(ThucydidesRunner.class)
@Story(Application.Backend.ProcessSales.class)
public class WorkWithBackendTest {
@Steps
public BackendSteps backend;
@Test
public void when_processing_a_sale_transation() {
backend.accepts_a_sale_transaction();
backend.should_the_update_mainframe();
}
}
----------------
=== Ajouter des étiquettes aux cas de test
vous pouvez ajouter des étiquettes arbitraires à vos tests à la fois pour JUnit et easyb. Les étiquettes fournissent un contexte aux tests. Une étiquette comporte deux parties - un +type+ et un +nom+. Les rapports Thucydides catégorisent les tests en se basant sur les types d'étiquettes indiqués.
Les types d'étiquettes sont arbitraires et vous pouvez ajouter autant de types que vous le souhaitez. Par défaut, un type d'étiquette +story+ est automatiquement ajouté à chaque test. Un exemple d'étiquettes dans les rapports Thucydides figure dans <<fig-tags-in-reports>>
[[fig-tags-in-report]]
.Les types d'étiquettes apparaissent en haut. Chaque type d'étiquette affiche les noms d'étiquettes.
image::figs/tags-in-reports.png[scaledwidth="80%", width=800]
==== Ajouter des étiquettes aux tests JUnit
Les étiquettes sont ajoutées aux tests JUnit en utilisant l'annotation +@WithTag+. Ce qui suit va ajouter une étiquette de type +epic+ avec le nom "Audit".
[source,java]
----------------
@WithTag(type="epic", name="Audit")
----------------
Si aucun type n'est défini, on suppose que le type d'étiquette par défaut est +feature+. En d'autres modes, les deux étiquettes suivantes sont équivalentes.
[source,java]
----------------
@WithTag(type="feature", name="Definition-lookup")
----------------
[source,java]
----------------
@WithTag(name="Definition-lookup")
----------------
+@WithTag+ possède une syntaxe alternative plus concise utilisant un deux points pour séparer le type et le nom de l'étiquette. Par exemple,
[source,java]
----------------
@WithTag("epic:Audit")
----------------
ou,
[source,java]
----------------
@WithTag("feature:Definition-lookup")
----------------
Plusieurs étiquettes peuvent être ajoutées en utilisant l'annotation +@WithTags+ ou sa cousine plus courte - +@WithTagValuesOf+. Par exemple,
[source,java]
----------------
@WithTags (
{
@WithTag(name="lookups", type="feature"),
@WithTag(name="release-2", type="release")
}
)
----------------
En utilisant +@WithTagValuesOf+, ce qui précède peut être écrit de la façon plus concise suivante :
[source,java]
----------------
@WithTagValuesOf({"lookups", "release:release-2"})
----------------
==== Ajouter des étiquettes aux tests easyb
Des étiquettes peuvent facilement être ajoutées aux histoires easyb sous la forme +thucydides.tests.<type>+. Par exemple,
[source,java]
----------------
thucydides.tests.feature "history reports"
thucydides.tests.epic "reporting"
thucydides.tests.epic "audit"
thucydides.tests.priority "high"
----------------
==== Filtrer les tests grâce aux étiquettes dans JUnit
Vous pouvez filtrer les tests par étiquette lors de l'exécution de Thucydides. Ceci peut être réalisé en indiquant en ligne de commande une seule étiquette ou une liste d'étiquettes séparées par des virgules. Si elle(s) est(sont) fournie(s), seules les classes et/ou les méthodes ayant une étiquette présente dans la liste seront exécutées.
Exemple:
----------------
mvn verify -Dtags="iteration:I1"
----------------
ou
----------------
mvn verify -Dtags="color:red,flavor:strawberry"
----------------
=== Exécuter Thucydides dans différents navigateurs
Thucydides gère tous les pilotes WebDriver de navigateurs, i.e. Firefox, Internet Explorer et Chrome ainsi que HTMLUnit. Par défaut, il utilisera Firefox. Cependant, vous pouvez passer outre cette option en utilisant la propriété système +webdriver.driver+. Pour l'utiliser en ligne de commandes, vous pouvez procéder comme suit:
-------------------
$ mvn test -Dwebdriver.driver=iexplorer
-------------------
Si vous n'utilisez pas Firefox par défaut, il vous sera utile de définir cette variable en tant que propriété dans votre fichier Maven pom.xml, par exemple:
-------------------
<properties>
<webdriver.driver>iexplorer</webdriver.driver>
</properties>
-------------------
Cependant, pour que ceci fonctionne avec JUnit, vous devez passer cette propriété webdriver.driver à JUnit. JUnit s'exécute dans une JVM dédiée et n'aura donc pas connaissance des propriétés système définies dans le build Maven. Pour y remédier, vous devez la passer explicitement à JUnit en utilisant l'option de configuration systemPropertyVariables, par exemple:
-------------------
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.7.1</version>
<configuration>
<systemPropertyVariables>
<webdriver.driver>${webdriver.driver}</webdriver.driver>
</systemPropertyVariables>
</configuration>
</plugin>
-------------------
==== Options Chrome
Thucydides gère la propriété système +chrome.switches+ pour définir des options pour le pilote Chrome. Ceci vous permet d'indiquer des options chrome très utiles telles que +"--homepage=about:blank" ou "--no-first-run"+. Vous pouvez indiquer autant d'options que vous voulez en les séparant par des virgules, par exemple :
-------------------
$mvn verify -Dchrome.switches="homepage=about:blank,--no-first-run"
-------------------
=== Forcer l'utilisation d'un pilote spécifique dans un cas de test ou dans un test
L'annotation +@Managed+ vous permet également d'indiquer le pilote que vous voulez utiliser pour un cas de test particulier, via l'attribut +driver+. Les valeurs gérées actuellement sont "firefox", "iexplorer", "chrome" et "htmlunit". L'attribut +driver+ vous permet de surcharger le pilote par défaut du système pour des exigences spécifiques. Par exemple, le cas de test suivant s'exécutera sous Chrome, quelle que soit la valeur de la propriété système +webdriver.driver+ utilisée:
-----------------
@RunWith(ThucydidesRunner.class)
@Story(Application.Search.SearchByKeyword.class)
public class SearchByFoodKeywordStoryTest {
@Managed(uniqueSession = true, driver="chrome")
public WebDriver webdriver;
@ManagedPages(defaultUrl = "http://www.google.co.nz")
public Pages pages;
@Steps
public EndUserSteps endUser;
@Test
public void searching_by_keyword_pears_should_display_the_corresponding_article() {
endUser.is_the_google_home_page();
endUser.enters("pears");
endUser.starts_search();
endUser.should_see_article_with_title_containing("Pear");
}
@Test
@WithDriver("firefox")
public void searching_by_keyword_pineapples_should_display_the_corresponding_article() {
endUser.is_the_google_home_page();
endUser.enters("pineapples");
endUser.starts_search();
endUser.should_see_article_with_title_containing("Pineapple");
}
}
----------------------
Avec *easyb*, vous pouvez utiliser la directive +use_driver+ comme illustré ici:
----------------------
using "thucydides"
...
thucydides.uses_default_base_url "http://localhost:9000"
thucydides.uses_driver chrome
...
scenario "The administrator adds a new category to the system",
{
given "a new category needs to be added to the system",
{
administrator.logs_in_to_admin_page_if_first_time()
administrator.opens_categories_list()
}
when "the administrator adds a new category",
{
administrator.selects_add_category()
administrator.adds_new_category("Scala Developers","SCALA")
}
then "the system should confirm that the category has been created",
{
administrator.should_see_confirmation_message "The Category has been created"
}
and "the new category should be visible to job seekers",
{
job_seeker.opens_jobs_page()
job_seeker.should_see_job_category "Scala Developers"
}
}
----------------------
Avec JUnit, vous pouvez également utiliser l'annotation +@WithDriver+ pour indiquer un pilote pour un test individuel. Ceci surchargera à la fois le pilote système et l'attribut pilote de l'annotation +@Managed+ s'il y en a. Par exemple, le test suivant s'exécutera toujours avec Firefox:
------------------