-
Notifications
You must be signed in to change notification settings - Fork 3
/
Tutorial_es.html
1054 lines (813 loc) · 35.7 KB
/
Tutorial_es.html
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
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Concordion - Tutorial (en español)</title>
<link media="all" rel="stylesheet" type="text/css" href="css/default.css"/>
<link media="print" rel="stylesheet" type="text/css" href="css/print.css"/>
<link rel="icon" type="image/vnd.microsoft.icon" href="favicon.ico" />
<style>
code a {
font-family: Courier New, Courier, Monospace;
}
h3 {
margin-top: 32px;
}
.section {
margin-top: 60px;
border-bottom: 1px dotted black;
padding-bottom: 8px;
}
.helpfulNote {
border: 1px solid gray;
padding: 8px;
margin-top: 30px;
background-color: #fffff0;
width: 600px;
}
.language {
margin-bottom: 12px;
}
.languageSelector {
float: right;
margin: 12px 0px 32px 32px;
font-size: 8pt;
}
</style>
</head>
<body>
<div class="page">
<div class="header">
<div id="google_translate_element" class="language-translation"></div>
<div class="logo"><a href="index.html"> <img src="image/front-page-banner.png" alt="Specification by Example" /> </a></div>
</div><!-- header -->
<div class="menuBar">
<ul class="menu">
<li><a href="/">Home</a></li>
<li><a href="Example.html">Example</a></li>
<li class="selectedTab"><a href="Tutorial.html">Tutorial</a></li>
<li><a href="Technique.html">Hints and Tips</a></li>
<li><a href="ExtensionsAPI.html">Extensions API</a></li>
<li><a href="Extensions.html">Extensions</a></li>
<li><a href="Download.html">Download</a></li>
<li><a href="Questions.html">FAQ</a></li>
</ul>
</div><!-- menuBar -->
<div class="content">
<div class="languageSelector">
<center>
<div class="language">
<a href="Tutorial.html"><img border="0" src="image/shared/Flag_en.png"/></a>
<a href="Tutorial.html"><br/>in English</a>
</div>
<div class="language">
<a href="Tutorial_es.html"><img border="0" src="image/shared/Flag_es.png"/></a>
<a href="Tutorial_es.html"><br/>en español</a>
</div>
</center>
</div>
<h1>Tutorial</h1>
<p>
Esta guía explica los mecanismos básicos para pasar de especificaciones
a <em>especificaciones activas</em> usando Concordion. No debería tomarle
más de 15 a 30 minutos el completarla, asumiendo que ya se está familiarizado
con Java, JUnit y XHTML.
</p>
<ul class="toc">
<li><a href="#installation">Instalación</a></li>
<li><a href="#basics">Los Fundamentos</a></li>
<li><code><a href="#assertEquals">concordion:assertEquals</a></code></li>
<li><code><a href="#set">concordion:set</a></code></li>
<li><code><a href="#execute">concordion:execute</a></code></li>
<li><code><a href="#executeTable">concordion:execute en una <table></a></code></li>
<li><code><a href="#verifyRows">concordion:verifyRows</a></code></li>
</ul>
<a name="installation">
</a><h2 class="section"><a name="installation">Instalación</a></h2>
<p>
Concordion requiere JDK 5.0 o superior y los siguientes JARs en el classpath:
</p>
<ul>
<li><b>concordion-1.5.1.jar</b></li>
<li><b>junit-4.12.jar</b> (o <b>junit-3.8.2.jar</b>) (<a class="externalLink" href="http://www.junit.org/">http://www.junit.org</a>)</li>
<li><b>hamcrest-core-1.3.jar</b> (<a class="externalLink" href="http://hamcrest.googlecode.com/">http://hamcrest.googlecode.com</a>) (requerido para JUnit 4.11 o más tarde)</li>
<li><b>ognl-2.6.9.jar</b> (<a class="externalLink" href="http://en.wikipedia.org/wiki/OGNL">http://en.wikipedia.org/wiki/OGNL</a>)</li>
<li><b>xom-1.2.5.jar</b> (<a class="externalLink" href="http://www.xom.nu/">http://www.xom.nu</a>)</li>
</ul>
<p>
Todos los JARs están incluidos en la
<a href="http://www.concordion.org/Download.html">distribución</a>.
</p>
<a name="basics">
</a><h2 class="section"><a name="basics"Los Fundamentos</a></h2>
<p>
<a name="basics"> Una especificación activa Concordion consiste en dos partes: (i) un documento
XHTML bien formado describiendo la funcionalidad, y (ii) código de fijación (<em>fixture</em>)
escrito en Java (una extensión especial de Concordion the un caso de prueba JUnit) que descubre
ejemplos concretos en el documento y los usa para verificar el sistema en pruebas (SUT). Ambos ficheros
deben estar en el mismo paquete (<em>package</em>).
</a></p>
<p>
<a name="basics"> Para que ocurra la magia, el documento debe ser antes <em>instrumentado</em> con comandos.
</a></p>
<p>
<p>
<a name="basics"> Los comandos Concordion son especificados como atributos de elementos en el documento
XHTML. Los navegadores web ignoran los atributos que no entienden, de modo que estos comandos son invisibles
a efectos prácticos.
</a></p>
<p>
<a name="basics"> Los comandos usan el espacio de nombres <code>"concordion"</code> definido al principio de
cada documento como sigue:
</a></p>
<pre class="html"><a name="basics"><html xmlns:concordion="http://www.concordion.org/2007/concordion">
</a></pre>
<p>
<a name="basics"> Empecemos con un ejemplo realmente sencillo...
</a></p>
<a name="assertEquals">
</a><h2 class="section"><a name="assertEquals">concordion:assertEquals</a></h2>
<ol>
<a name="assertEquals"> </a><li>
<a name="assertEquals"> Creemos un paquete Java llamado <code>"example"</code>.
</a></li>
<a name="assertEquals"> </a><li>
<a name="assertEquals"> Creemos un fichero <code>"HelloWorld.html"</code> dentro del paquete conteniendo:
</a><pre class="html"><a name="assertEquals"><html>
<body>
<p>Hello World!</p>
</body>
</html>
</a></pre>
<a name="assertEquals"> </a></li>
<a name="assertEquals"> </a><li>
<a name="assertEquals"> Ahora instrumentemos el fichero como sigue:
</a><pre class="html"><a name="assertEquals"><html <b>xmlns:concordion="http://www.concordion.org/2007/concordion"</b>>
<body>
<p <b>concordion:assertEquals="getGreeting()"</b>>Hello World!</p>
</body>
</html>
</a></pre>
<a name="assertEquals"> </a></li>
<a name="assertEquals"> </a><li>
<a name="assertEquals"> En el mismo paquete <code>example</code>, creemos un fichero Java
<code>"HelloWorldTest.java"</code> conteniendo:
</a><pre class="java"><a name="assertEquals">package example;
import org.concordion.integration.junit3.ConcordionTestCase;
public class HelloWorldTest extends ConcordionTestCase {
public String getGreeting() {
return "Hello World!";
}
}
</a></pre>
<a name="assertEquals"> </a></li>
<a name="assertEquals"> </a><li>
<a name="assertEquals"> Ahora ejecutemos la clase <code>HelloWorldTest</code> usando JUnit.
</a></li>
</ol>
<p>
<a name="assertEquals"> Si lo ha hecho correctamente, JUnit debería mostrarle una barra en verde y un
mensaje como el que sigue debería ser mostrado en la consola:
</a></p>
<pre class="console"><a name="assertEquals">C:\temp\concordion-output\example\HelloWorld.html
Successes: 1 Failures: 0
</a></pre>
<p>
<a name="assertEquals"> El mensaje muestra la trayectoria del fichero de salida (resultado) para
la prueba y un resumen con los contadores de éxitos y fracasos en las pruebas. Por defecto, Concordion
produce la salida en el directorio especificado en la propiedad del sistema
<code>java.io.tmpdir</code>.
</a></p>
<p>
<a name="assertEquals"> Abra el fichero de salida en un navegador y podrá ver el mismo
contenido que en el fichero de entrada, pero con las palabras <code>Hello World!</code>
resaltadas en verde.
</a></p>
<img style="padding-right: 0px" src="image/tutorial/HelloWorldSuccess.png" alt="Hello World! successful outcome"/>
</a><h3><a name="assertEquals">Propiedades Java Bean</a></h3>
<p>
<a name="assertEquals"> En el ejemplo de arriba, la llamada a <code>"getGreeting()"</code> puede
simplificarse a <code>"greeting"</code> ya que el lenguaje de expresiones de Concordion
puede manejar propiedades sencillas del tipo Java Bean.
</a></p>
<p>
<pre class="html"><a name="assertEquals"><html xmlns:concordion="http://www.concordion.org/2007/concordion">
<body>
<p <b>concordion:assertEquals="greeting"</b>>Hello World!</p>
</body>
</html>
</a></pre>
<a name="set">
</a><h2 class="section"><a name="set">concordion:set</a></h2>
<p>
<a name="set"> Dada una especificación como ésta:
</a></p>
<pre class="html"><a name="set"><html>
<body>
<p>
El saludo para el usuario Pepe será: ¡Hola Pepe!
</p>
</body>
</html>
</a></pre>
<p>
<a name="set"> Queremos que el nombre ("Pepe") sea un parámetro
y que el saludo ("¡Hola Pepe!") sea verificado contra el
resultado devuelto por el sistema.
</a></p>
<p>
<a name="set"> Para hacer esto ponemos etiquetas <code><span></code>
alrededor de los dos textos relevantes dentro del documento. En HTML, las
etiquetas <code><span></code> no tienen ningún efecto al mostrar la
salida del documento.
</a></p>
<pre class="html"><a name="set"><html>
<body>
<p>
El usuario para el usuario <b><span></b>Pepe<b></span></b>
será: <b><span></b>¡Hola Pepe!<b></span></b>
</p>
</body>
</html>
</a></pre>
<p>
<a name="set"> Ahora podemos instrumentar el documento:
</a></p>
<pre class="html"><a name="set"><html <b>xmlns:concordion="http://www.concordion.org/2007/concordion"</b>>
<body>
<p>
El saludo para el usuario <span <b>concordion:set="#firstName"</b>>Pepe</span>
será:
<span <b>concordion:assertEquals="greetingFor(#firstName)"</b>>¡Hola Pepe!</span>
</p>
</body>
</html>
</a></pre>
<p>
<a name="set"> Cuando Concordion procesa el documento, temporalmente establecerá
el valor de la variable <code>#firstName</code> a <code>"Pepe"</code> y entonces
llamará al método <code>greetingFor()</code> con ese valor y comprobará que el
resultado es igual que <code>"¡Hola Pepe!"</code>.
</a></p>
<p>
<a name="set"> Nuestro código Java de la <em>fixture</em> necesitará ser modificado:
</a></p>
<pre class="java"><a name="set">package example;
import org.concordion.integration.junit3.ConcordionTestCase;
public class HelloWorldTest extends ConcordionTestCase {
public String greetingFor(String firstName) {
return "BLA";
}
}
</a></pre>
<p>
<a name="set"> Justo igual que cuando escribimos pruebas unitarias, siempre
hacemos fallar la prueba antes de hacer que pase, para darnos confianza en
que estamos realmente probando algo.
Con el código tal y como está ahora deberíamos obtener un fallo (pues esperamos
"¡Hola Pepe!" pero obtenemos "BLA").
</a></p>
<p>
<a name="set"> Ahora arreglamos el código:
</a></p>
<pre class="java"><a name="set">package example;
import org.concordion.integration.junit3.ConcordionTestCase;
public class HelloWorldTest extends ConcordionTestCase {
public String greetingFor(String firstName) {
return "¡Hola " + firstName + "!";
}
}
</a></pre>
<p>
<a name="set"> Obviamente, en una aplicación real, la implementación de <code>greetingFor()</code>
sería bastante diferente. El comportamiento no estaría implementado aquí sino en el código del sistema
que estamos probando y la <em>fixture</em> simplemente llamaría al sistema. Esto se podría hacer a nivel
del sistema o a un nivel más bajo: se podría incluso llamar directamente como en una prueba unitaria
si escribir una prueba de sistema fuera muy lento o difícil de realizar.
</a></p>
<a name="execute">
</a><h2 class="section"><a name="execute">concordion:execute</a></h2>
<p>
<a name="execute"> El comando execute tiene tres usos principales:
</a></p>
<ol>
<a name="execute"> </a><li><a href="#executeVoid">Ejecutar una instrucción cuyo resultado es "void"</a>.</li>
<li><a href="#executeObjectResult">Ejecutar una instrucción cuyo resultado es un objeto</a>
(para permitir comprobar varias propiedades de dicho objeto).
</li>
<li><a href="#executeUnusualSentences">Manejar frases con estructuras poco habituales</a>.</li>
</ol>
<a name="executeVoid">
</a><h3><a name="executeVoid">Ejecutar una instrucción cuyo resultado es <code>void</code></a></h3>
<p>
<a name="executeVoid"> En ocasiones puede ser útil ejecutar una
instrucción que establezca de alguna manera el estado del sistema.
Cada vez que hacemos esto, sin embargo, deberían sonar alarmas en
nuestras cabezas y preguntarnos si no estaremos (inadvertidamente)
escribiendo un script en vez de una especificación. Por ejemplo, una
llamada a <code>"clearDatabase()"</code> sería un evidente mal uso (ver
</a><a href="http://www.concordion.org/Technique.html">Técnicas (en inglés)</a> para
saber más sobre este tema).
</p>
<p>
Como regla general, los métodos que devuelven <code>void</code> llamados desde un <code>execute</code>
deberían comenzar con la palabra <code>set</code> o <code>setUp</code>. P.ej. <code>setUpUser(#username)</code>.
</p>
<p>
Tomemos la siguiente especifación como ejemplo:
</p>
<pre class="html"><html <b>xmlns:concordion="http://www.concordion.org/2007/concordion"</b>>
<body>
<p>
Si la hora es:
<span <b>concordion:set="#time"</b>>09:00AM</span>
<span <b>concordion:execute="setCurrentTime(#time)"</b> />
entonces el saludo será:
<span <b>concordion:assertEquals="getGreeting()"</b>>¡Buenos Días Mundo!</span>
</p>
</body>
</html>
</pre>
<p>
Nuestro código Java de la <em>fixture</em> será como éste:
</p>
<pre class="java">package example;
import org.concordion.integration.junit3.ConcordionTestCase;
public class HelloWorldTest extends ConcordionTestCase {
public void setCurrentTime(String time) {
// TODO
}
public String getGreeting() {
return "TODO";
}
}
</pre>
<p>
Realmente nosotros podemos eliminar la necesidad de usar ese
comando <code>concordion:set</code> usando en su lugar la variable
especial <code>#TEXT</code> (la cuál contiene el texto del elemento
actual). La instrumentación abreviada queda así:
</p>
<pre class="html"><html xmlns:concordion="http://www.concordion.org/2007/concordion">
<body>
<p>
Si la hora es:
<span <b>concordion:execute="setCurrentTime(#TEXT)"</b>>09:00AM</span>
entonces el saludo será:
<span <b>concordion:assertEquals="getGreeting()"</b>>¡Buenos Días Mundo!</span>
</p>
</body>
</html>
</pre>
<p>
Una alternativa sería cambiar la firma del método
<code>getGreeting()</code> para permitir pasarle la hora
como un parámetro. Este el camino que normalmente tomaríamos.
Un <code>execute</code> sin valor de retorno es una mala
señal; p.ej. estamos escribiendo un script o nuestra especificación
contiene demasiadas variables y cubre demasiados comportamientos.
Sin embargo, la funcionalidad está ahí por si la necesitamos.
</p>
<a name="executeObjectResult">
</a><h3><a name="executeObjectResult">Ejecutar una instrucción cuyo resultado es un objeto</a></h3>
<p>
<a name="executeObjectResult"> A veces necesitamos comprobar más de un resultado
para un comportamiento dado. Por ejemplo, aquí queremos comprobar que tanto el
nombre como el apellido son extraídos correctamente a partir del nombre completo:
</a></p>
<pre class="html"><a name="executeObjectResult"><html <b>xmlns:concordion="http://www.concordion.org/2007/concordion"</b>>
<head>
<link href="../concordion.css" rel="stylesheet" type="text/css" />
</head>
<body>
<h1>Separando Nombres</h1>
<p>
Para ayudar a personalizar nuestros envíos postales, queremos tener
el nombre y el apellido del cliente. Desgraciadamente los datos del
cliente que nos han proporcionado sólo contienen nombres completos.
</p>
<p>
Por lo tanto el sistema intenta separar un nombre completo en sus
componentes separándolos por el espacio en blanco.
</p>
<div class="example">
<h3>Ejemplo</h3>
<p>
El nombre completo
<span <b>concordion:execute="#result = split(#TEXT)"</b>>Juan Pérez</span>
se separará en nombre
<span <b>concordion:assertEquals="#result.firstName"</b>>Juan</span>
y apellido
<span <b>concordion:assertEquals="#result.lastName"</b>>Pérez</span>.
</p>
</div>
</body>
</html>
</a></pre>
<p>
<a name="executeObjectResult"> La variable <code>#result</code> será un objeto devuelto
por el método <code>split()</code>. Este objeto tendrá unas propiedades
<code>firstName</code> y <code>lastName</code>.
</a></p>
<p>
<a name="executeObjectResult"> Asumiendo que nuestro fichero HTML está en el paquete <code>example</code> y que
se llama "SplittingNames.html", entonces necesitamos una <em>fixture</em> Java llamada
<code>SplittingNamesTest</code>:
</a></p>
<pre class="java"><a name="executeObjectResult">package example;
import org.concordion.integration.junit3.ConcordionTestCase;
public class SplittingNamesTest extends ConcordionTestCase {
}
</a></pre>
<p>
<a name="executeObjectResult"> Si ejecutamos la fixture tal y como está (e.d. vacía), la salida sería así:
</a></p>
<div>
<a name="executeObjectResult"><img src="image/tutorial/execute/BrokenDueToMissingFixtureCode_es.png" alt="La salida muestra un test roto (debido a la ausencia de código de fijación)">
</a></div>
<p>
<a name="executeObjectResult"> Nos dice lo que tenemos que hacer. Arreglamos nuestro código de fijación:
</a></p>
<pre class="java"><a name="executeObjectResult">package example;
import org.concordion.integration.junit3.ConcordionTestCase;
public class SplittingNamesTest extends ConcordionTestCase {
public Result split(String fullName) {
return new Result();
}
class Result {
public String firstName = "TODO";
public String lastName = "TODO";
}
}
</a></pre>
<p>
<a name="executeObjectResult"> Lo ejecutamos ahora y obtenemos:
</a></p>
<div>
<a name="executeObjectResult"><img src="image/tutorial/execute/BrokenBecauseNotFullyImplemented_es.png" alt="La salida muestra un test roto porque la fixture no está completamente implementada">
</a></div>
<p>
<a name="executeObjectResult"> Implementemos la función. Obviamente, la implementación debería estar
en el sistema real, no en el caso de prueba, pero sólo estamos jugando...
</a></p>
<pre class="java"><a name="executeObjectResult">package example;
import org.concordion.integration.junit3.ConcordionTestCase;
public class SplittingNamesTest extends ConcordionTestCase {
public Result split(String fullName) {
Result result = new Result();
String[] words = fullName.split(" ");
result.firstName = words[0];
result.lastName = words[1];
return result;
}
class Result {
public String firstName;
public String lastName;
}
}
</a></pre>
<p>
<a name="executeObjectResult"> Ahora el test pasa:
</a></p>
<div>
<a name="executeObjectResult"><img src="image/tutorial/execute/Successful_es.png" alt="Ahora el test pasa">
</a></div>
<p>
<a name="executeObjectResult"> Nota: Nuestra inner class <code>Result</code> podría también ser implementada
con getters en vez de con campos públicos: la instrumentación del HTML no cambia por ello.
</a></p>
<pre class="java"><a name="executeObjectResult">class Result {
private final String firstName;
private final String lastName;
public Result(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
public String getFirstName() {
return firstName;
}
public String getLastName() {
return lastName;
}
}
</a></pre>
<a name="executeUnusualSentences">
</a><h3><a name="executeUnusualSentences">Manejar frases con estructuras poco habituales</a></h3>
<p>
<a name="executeUnusualSentences"> Una de las grandes cosas de Concordion es que
cuando estás escribiendo las especificaciones no te tienes que preocupar acerca
de cómo las vas a instrumentar. Simplemente te concentras en hacer el documento
lo más legible posible.
</a></p>
<p>
<a name="executeUnusualSentences"> La mayoría de las frases se pueden instrumentar.
Si no puedes encontrar cómo instrumentarla, siempre puedes jugar con las palabras,
pero en general esto no debería ser necesario. El comando <code>execute</code>
proporciona esta flexibilidad.
</a></p>
<p>
<a name="executeUnusualSentences"> Por ejemplo, digamos que tenemos la especificación:
</a></p>
<pre class="html"><a name="executeUnusualSentences"><p>
Tras acceder al sistema, el saludo que recibirá el usuario <span>Pepe</span>
será: <span>¡Hola Pepe!</span>
</p>
</a></pre>
<p>
<a name="executeUnusualSentences"> Esto es fácil de instrumentar:
</a></p>
<pre class="html"><a name="executeUnusualSentences"><p>
Tras acceder al sistema, el saludo que recibirá el usuario
<span <b>concordion:set="#firstName"</b>>Pepe</span>
será:
<span <b>concordion:assertEquals="greetingFor(#firstName)"</b>>¡Hola Pepe!</span>
</p>
</a></pre>
<p>
<a name="executeUnusualSentences"> Pero que hubiera pasado si nuestra
especificación se hubiera escrito como sigue:
</a></p>
<pre class="html"><a name="executeUnusualSentences"><p>
Se debería mostrar el saludo "<span>¡Hola Pepe!</span>" al usuario
<span>Pepe</span> cuando éste acceda al sistema.
</p>
</a></pre>
<p>
<a name="executeUnusualSentences"> En este caso, el parámetro de entrada <code>Pepe</code>
aparece <em>después</em> de la salida que queremos comprobar (el saludo). Podemos
resolver este problema usando un comando <code>execute</code> en el elemento
más exterior (el <code><p></code>).
</a></p>
<pre class="html"><a name="executeUnusualSentences"><p <b>concordion:execute="#greeting = greetingFor(#firstName)"</b>>
Se debería mostrar el saludo "<span <b>concordion:assertEquals="#greeting"</b>>¡Hola Pepe!</span>"
al usuario <span <b>concordion:set="#firstName"</b>>Pepe</span>
cuando éste acceda al sistema.
</p>
</a></pre>
<p>
<a name="executeUnusualSentences"> ¿Cómo funciona esto? Esto funciona porque el comando
<code>execute</code> está diseñado para procesar comandos en sus elementos hijo en un
orden especial. Antes que nada procesa cualquier comando <code>set</code> hijo, entonces
ejecuta su propio comando y finalmente procesa cualquier comando <code>assertEquals</code> hijo.
</a></p>
<a name="executeTable">
</a><h2 class="section"><a name="executeTable">concordion:execute en una <table></a></h2>
<p>
<a name="executeTable"> Cuando queremos mostrar varios ejemplos de un
comportamiento, repitiendo la misma estructura de una frase una y otra vez,
probablemente esto no sea muy fácil de leer. Sería mejor usar una tabla.
</a></p>
<p>
<a name="executeTable"> Por ejemplo:
</a></p>
<div>
<a name="executeTable"><img src="image/tutorial/executeTable/HowTableIsDisplayed_es.png" alt="Cómo se muestra la tabla (simple y claro)">
</a></div>
<p>
<a name="executeTable"> Podemos instrumentar esta tabla, de una manera un poco larga,
como sigue:
</a></p>
<pre class="html"><a name="executeTable"><html xmlns:concordion="http://www.concordion.org/2007/concordion">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<link href="../concordion.css" rel="stylesheet" type="text/css" />
</head>
<body>
<h1>Separando Nombres</h1>
<p>
Para ayudar a personalizar nuestros envíos postales, queremos tener
el nombre y el apellido del cliente. Desgraciadamente los datos del
cliente que nos han proporcionado sólo contienen nombres completos.
</p>
<p>
Por lo tanto el sistema intenta separar un nombre completo en sus
componentes separándolos por el espacio en blanco.
</p>
<div class="example">
<h3>Ejemplos</h3>
<table>
<tr>
<th>Nombre Completo</th>
<th>Nombre</th>
<th>Apellido</th>
</tr>
<tr <b>concordion:execute="#result = split(#fullName)"</b>>
<td <b>concordion:set="#fullName"</b>>Juan Pérez</td>
<td <b>concordion:assertEquals="#result.firstName"</b>>Juan</td>
<td <b>concordion:assertEquals="#result.lastName"</b>>Pérez</td>
</tr>
<tr <b>concordion:execute="#result = split(#fullName)"</b>>
<td <b>concordion:set="#fullName"</b>>Felipe Reyes</td>
<td <b>concordion:assertEquals="#result.firstName"</b>>Felipe</td>
<td <b>concordion:assertEquals="#result.lastName"</b>>Reyes</td>
</tr>
</table>
</div>
</body>
</html>
</a></pre>
<p>
<a name="executeTable"> Sin embargo, esto es repetitivo, por lo que Concordion proporciona
una alternativa más cómoda.
Cuando ponemos un comando <code>execute</code> en un elemento <code><table></code>,
los elementos de la cabecera (la fila que contiene los elementos <th>) son copiados
a cada linea de detalle (las filas que contienen los elementos <td>) y se ejecuta
el comando <code>execute</code> en cada linea de detalle.
</a></p>
<pre class="html"><a name="executeTable"><html xmlns:concordion="http://www.concordion.org/2007/concordion">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<link href="../concordion.css" rel="stylesheet" type="text/css" />
</head>
<body>
<h1>Separando Nombres</h1>
<p>
Para ayudar a personalizar nuestros envíos postales, queremos tener
el nombre y el apellido del cliente. Desgraciadamente los datos del
cliente que nos han proporcionado sólo contienen nombres completos.
</p>
<p>
Por lo tanto el sistema intenta separar un nombre completo en sus
componentes separándolos por el espacio en blanco.
</p>
<div class="example">
<h3>Ejemplos</h3>
<table <b>concordion:execute="#result = split(#fullName)"</b>>
<tr>
<th <b>concordion:set="#fullName"</b>>Nombre Completo</th>
<th <b>concordion:assertEquals="#result.firstName"</b>>Nombre</th>
<th <b>concordion:assertEquals="#result.lastName"</b>>Apellido</th>
</tr>
<tr>
<td>Juan Pérez</td>
<td>Juan</td>
<td>Pérez</td>
</tr>
<tr>
<td>Felipe Reyes</td>
<td>Felipe</td>
<td>Reyes</td>
</tr>
</table>
</div>
</body>
</html>
</a></pre>
<p>
<a name="executeTable"> Esta instrumentación tiene el mismo comportamiento
que la del ejemplo anterior.
</a></p>
<a name="verifyRows">
</a><h2 class="section"><a name="verifyRows">concordion:verifyRows</a></h2>
<p>
<a name="verifyRows"> Algunas veces queremos comprobar los contenidos de una
colección de resultados que devuelve el sistema. En el framework Fit usaríamos
para esto una <code>RowFixture</code>. En Concordion usamos el comando
<code>verifyRows</code>.
</a></p>
<p>
<a name="verifyRows"> Por ejemplo, mientras escribimos una herramienta de
administración de usuarios, podríamos escribir una especificación como la
que sigue en la que describimos el comportamiento de la funcionalidad de
búsqueda:
</a></p>
<blockquote>
<a name="verifyRows"><img src="image/tutorial/verifyRows/partialMatches/OriginalTable_es.png" alt="Especificación Original">
</a></blockquote>
<p>
<a name="verifyRows"> La idea es que en el código de fijación añadamos
los usuarios al sistema, realicemos una búsqueda y entonces confirmemos
que en los resultados de búsqueda obtenemos los usuarios correctos (y sólo
estos usuarios). Si hay usuarios de más o de menos, o no son los usuarios
esperados, queremos que el test falle.
</a></p>
<p>
<a name="verifyRows"> El código HTML instrumentado para esta especificación es como sigue:
</a></p>
<pre class="html"><a name="verifyRows"><html <b>xmlns:concordion="http://www.concordion.org/2007/concordion"</b>>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
<h1>Correspondencias Parciales</h1>
<p>
Las búsquedas por nombre de usuario devuelven correspondencias parciales, e.d.,
se devuelven todos los nombres de usuario que contienen la cadena de búsqueda.
</p>
<div class="example">
<h3>Ejemplo</h3>
<p>Dados estos usuarios:</p>
<table <b>concordion:execute="setUpUser(#username)"</b>>
<tr><th <b>concordion:set="#username"</b>>Nombre de usuario</th></tr>
<tr><td>john.lennon</td></tr>
<tr><td>ringo.starr</td></tr>
<tr><td>george.harrison</td></tr>
<tr><td>paul.mccartney</td></tr>
</table>
<p>La búsqueda por "<b <b>concordion:set="#searchString"</b>>arr</b>" devolverá:</p>
<table <b>concordion:verifyRows="#username : getSearchResultsFor(#searchString)"</b>>
<tr><th <b>concordion:assertEquals="#username"</b>>Nombres de usuario con correspondencia</th></tr>
<tr><td>george.harrison</td></tr>
<tr><td>ringo.starr</td></tr>
</table>
</div>
</body>
</html>
</a></pre>
<p>
<a name="verifyRows"> La sintaxis del comando <code>verifyRows</code> es:
</a></p>
<pre><a name="verifyRows">#loopVar : expression
</a></pre>
<p>
<a name="verifyRows"> Donde <code>expression</code> devuelve un objeto
<code>Iterable</code> que se recorre en un orden predecible,
(p.ej. una <code>List</code>, <code>LinkedHashSet</code> o un
<code>TreeSet</code>).
Y <code>#loopVar</code> proporciona acceso al objeto actual durante
el recorrido y permite al método <code>assertEquals</code> comprobar
su valor.
</a></p>
<p>
<a name="verifyRows"> El orden de los elementos de la tabla que se verifica
deben corresponder con el orden del recorrido que nos devuelve <code>expression</code>.
Necesitamos ordenar los elementos para asegurar que están en un orden conocido
y consistente. En nuestro ejemplo, estamos usando el orden alfabético
("george" antes que "ringo").
</a></p>
<p>
<a name="verifyRows"> El esqueleto del código de fijación sería como sigue:
</a></p>
<pre class="java"><a name="verifyRows">public class PartialMatchesTest extends ConcordionTestCase {
public void setUpUser(String username) {
// TODO: Dar de alta al usuario en el sistema
}
public Iterable<String> getSearchResultsFor(String searchString) {
// TODO: Realizar la búsqueda y devolver los resultados reales
return new ArrayList<String>();
}
}
</a></pre>
<p>
<a name="verifyRows"> Si ejecutamos el test con este esqueleto obtenemos:
</a></p>
<blockquote>
<a name="verifyRows"><img src="image/tutorial/verifyRows/partialMatches/WhenNoRowsReturned_es.png" alt="Faltan dos filas">
</a></blockquote>
<p>
<a name="verifyRows"> Faltan dos filas porque nuestra función de búsqueda no está implementada
y devuelve un conjunto vacío.
</a></p>
<p>
<a name="verifyRows"> Sólo para demostrar cómo funciona, implementemos la funcionalidad dentro
del código de fijación (en vez de llamar al sistema, que sería lo correcto):
</a></p>
<pre class="java"><a name="verifyRows">public class PartialMatchesTest extends ConcordionTestCase {
private Set<String> usernamesInSystem = new HashSet<String>();
public void setUpUser(String username) {
usernamesInSystem.add(username);
}
public Iterable<String> getSearchResultsFor(String searchString) {