Esta guía explica los mecanismos básicos para pasar de especificaciones a especificaciones activas 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 HTML.
concordion:assertEqualsconcordion:setconcordion:executeconcordion:execute en una <table>concordion:verifyRowsConcordion requiere JDK 5.0 o superior y los siguientes JARs en el classpath:
Nota: Todos los JARs están incluidos en la distribución.
Para que ocurra la magia, el documento debe ser antes instrumentado con comandos.
<html xmlns:concordion="http://www.concordion.org/2007/concordion">
Empecemos con un ejemplo realmente sencillo...
"example".
"HelloWorld.html" dentro del paquete conteniendo:
<html> <body> <p>Hello World!</p> </body> </html>
<html xmlns:concordion="http://www.concordion.org/2007/concordion"> <body> <p concordion:assertEquals="getGreeting()">Hello World!</p> </body> </html>
example, creemos un fichero Java
"HelloWorldTest.java" conteniendo:
package example; import org.concordion.integration.junit3.ConcordionTestCase; public class HelloWorldTest extends ConcordionTestCase { public String getGreeting() { return "Hello World!"; } }
HelloWorldTest usando JUnit.
C:\temp\concordion-output\example\HelloWorld.html Successes: 1 Failures: 0
<html xmlns:concordion="http://www.concordion.org/2007/concordion"> <body> <p concordion:assertEquals="greeting">Hello World!</p> </body> </html>
Dada una especificación como ésta:
<html> <body> <p> El saludo para el usuario Pepe será: ¡Hola Pepe! </p> </body> </html>
<html> <body> <p> El usuario para el usuario <span>Pepe</span> será: <span>¡Hola Pepe!</span> </p> </body> </html>
Ahora podemos instrumentar el documento:
<html xmlns:concordion="http://www.concordion.org/2007/concordion"> <body> <p> El saludo para el usuario <span concordion:set="#firstName">Pepe</span> será: <span concordion:assertEquals="greetingFor(#firstName)">¡Hola Pepe!</span> </p> </body> </html>
Nuestro código Java de la fixture necesitará ser modificado:
package example; import org.concordion.integration.junit3.ConcordionTestCase; public class HelloWorldTest extends ConcordionTestCase { public String greetingFor(String firstName) { return "BLA"; } }
package example; import org.concordion.integration.junit3.ConcordionTestCase; public class HelloWorldTest extends ConcordionTestCase { public String greetingFor(String firstName) { return "¡Hola " + firstName + "!"; } }
El comando execute tiene tres usos principales:
void
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 "clearDatabase()" sería un evidente mal uso (ver
Técnicas (en inglés) para
saber más sobre este tema).
Como regla general, los métodos que devuelven void llamados desde un execute
deberían comenzar con la palabra set o setUp. P.ej. setUpUser(#username).
Tomemos la siguiente especifación como ejemplo:
<html xmlns:concordion="http://www.concordion.org/2007/concordion">
<body>
<p>
Si la hora es:
<span concordion:set="#time">09:00AM</span>
<span concordion:execute="setCurrentTime(#time)" />
entonces el saludo será:
<span concordion:assertEquals="getGreeting()">¡Buenos Días Mundo!</span>
</p>
</body>
</html>
Nuestro código Java de la fixture será como éste:
package example;
import org.concordion.integration.junit3.ConcordionTestCase;
public class HelloWorldTest extends ConcordionTestCase {
public void setCurrentTime(String time) {
// TODO
}
public String getGreeting() {
return "TODO";
}
}
Realmente nosotros podemos eliminar la necesidad de usar ese
comando concordion:set usando en su lugar la variable
especial #TEXT (la cuál contiene el texto del elemento
actual). La instrumentación abreviada queda así:
<html xmlns:concordion="http://www.concordion.org/2007/concordion">
<body>
<p>
Si la hora es:
<span concordion:execute="setCurrentTime(#TEXT)">09:00AM</span>
entonces el saludo será:
<span concordion:assertEquals="getGreeting()">¡Buenos Días Mundo!</span>
</p>
</body>
</html>
Una alternativa sería cambiar la firma del método
getGreeting() para permitir pasarle la hora
como un parámetro. Este el camino que normalmente tomaríamos.
Un execute 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.
<html xmlns:concordion="http://www.concordion.org/2007/concordion"> <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 concordion:execute="#result = split(#TEXT)">Juan Pérez</span> se separará en nombre <span concordion:assertEquals="#result.firstName">Juan</span> y apellido <span concordion:assertEquals="#result.lastName">Pérez</span>. </p> </div> </body> </html>
package example; import org.concordion.integration.junit3.ConcordionTestCase; public class SplittingNamesTest extends ConcordionTestCase { }
Si ejecutamos la fixture tal y como está (e.d. vacía), la salida sería así:
Nos dice lo que tenemos que hacer. Arreglamos nuestro código de fijación:
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"; } }
Lo ejecutamos ahora y obtenemos:
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; } }
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; } }
Por ejemplo, digamos que tenemos la especificación:
<p> Tras acceder al sistema, el saludo que recibirá el usuario <span>Pepe</span> será: <span>¡Hola Pepe!</span> </p>
Esto es fácil de instrumentar:
<p> Tras acceder al sistema, el saludo que recibirá el usuario <span concordion:set="#firstName">Pepe</span> será: <span concordion:assertEquals="greetingFor(#firstName)">¡Hola Pepe!</span> </p>
Pero que hubiera pasado si nuestra especificación se hubiera escrito como sigue:
<p> Se debería mostrar el saludo "<span>¡Hola Pepe!</span>" al usuario <span>Pepe</span> cuando éste acceda al sistema. </p>
<p concordion:execute="#greeting = greetingFor(#firstName)"> Se debería mostrar el saludo "<span concordion:assertEquals="#greeting">¡Hola Pepe!</span>" al usuario <span concordion:set="#firstName">Pepe</span> cuando éste acceda al sistema. </p>
Podemos instrumentar esta tabla, de una manera un poco larga, como sigue:
<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 concordion:execute="#result = split(#fullName)"> <td concordion:set="#fullName">Juan Pérez</td> <td concordion:assertEquals="#result.firstName">Juan</td> <td concordion:assertEquals="#result.lastName">Pérez</td> </tr> <tr concordion:execute="#result = split(#fullName)"> <td concordion:set="#fullName">Felipe Reyes</td> <td concordion:assertEquals="#result.firstName">Felipe</td> <td concordion:assertEquals="#result.lastName">Reyes</td> </tr> </table> </div> </body> </html>
<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 concordion:execute="#result = split(#fullName)"> <tr> <th concordion:set="#fullName">Nombre Completo</th> <th concordion:assertEquals="#result.firstName">Nombre</th> <th concordion:assertEquals="#result.lastName">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>
Esta instrumentación tiene el mismo comportamiento que la del ejemplo anterior.
El código HTML instrumentado para esta especificación es como sigue:
<html xmlns:concordion="http://www.concordion.org/2007/concordion"> <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 concordion:execute="setUpUser(#username)"> <tr><th concordion:set="#username">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 concordion:set="#searchString">arr</b>" devolverá:</p> <table concordion:verifyRows="#username : getSearchResultsFor(#searchString)"> <tr><th concordion:assertEquals="#username">Nombres de usuario con correspondencia</th></tr> <tr><td>george.harrison</td></tr> <tr><td>ringo.starr</td></tr> </table> </div> </body> </html>
La sintaxis del comando verifyRows es:
#loopVar : expression
El esqueleto del código de fijación sería como sigue:
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>(); } }
Si ejecutamos el test con este esqueleto obtenemos:
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) { SortedSet<String> matches = new TreeSet<String>(); for (String username : usernamesInSystem) { if (username.contains(searchString)) { matches.add(username); } } return matches; } }
Ahora cuando ejecutamos el test obtenemos un éxito:
Éstas son las características esenciales de Concordion y deberían ser todo lo que necesitaramos para comenzar. En la página Technique podemos encontrar consejos sobre cómo enfocar adecuadamente nuestras especificaciones.
Privacy Policy | Copyright © 2007-2008 David Peterson. All rights reserved.