Ce diaporama a bien été signalé.
Nous utilisons votre profil LinkedIn et vos données d’activité pour vous proposer des publicités personnalisées et pertinentes. Vous pouvez changer vos préférences de publicités à tout moment.
SPOCKSPOCK
Pruebas en Java con Groovy yPruebas en Java con Groovy y
Andrés ViedmaAndrés Viedma
¿Quién soy?¿Quién soy?
Dinosaurio del software
más de 20 años como profesional
Javero inquieto
Sospechoso habitual del Mad...
Pero... ¿de verdad hacemosPero... ¿de verdad hacemos
pruebas?pruebas?
EL CASTIGADOR DE LOS TESTSEL CASTIGADOR DE LOS TESTS
EL CASTIGADOR DE LOS TESTSEL CASTIGADOR DE LOS TESTS
Da su merecido (o sea, pruebas) a
todas las líneas de código
No hace ...
ROBIN HOOD, EL INFALIBLEROBIN HOOD, EL INFALIBLE
ROBIN HOOD, EL INFALIBLEROBIN HOOD, EL INFALIBLE
Nunca falla un tiro.
Ni tampoco falla en el código.
Las pruebas son para ...
EL INCREIBLE PINOCHOEL INCREIBLE PINOCHO
EL INCREIBLE PINOCHOEL INCREIBLE PINOCHO
Hace muchíiiiisimas pruebas.
No se lo cree ni él.
EL INFORMÁTICO VAGOEL INFORMÁTICO VAGO
EL INFORMÁTICO VAGOEL INFORMÁTICO VAGO
Hacer pruebas es
importante.
EL INFORMÁTICO VAGOEL INFORMÁTICO VAGO
Hacer pruebas es
importante.
Es una pena que
también sea
UN COÑAZO
¿Y TÚ?...¿Y TÚ?...
Tests a prueba de VagosTests a prueba de Vagos
Subir nivel de abstracción
No “programar tests” → declarar casos de prueba
...
Mirando SPOCKMirando SPOCK
SPOCKSPOCK
Hecho en Groovy
SPOCKSPOCK
Hecho en Groovy
SPOCKSPOCK
Muy parecido a Java (“extensión” del lenguaje)
Compatible con él (se ejecuta en JVM)
Lenguaje dinámico (o no)
M...
¡Uf! Para montar eso
voy a necesitar...
¡Uf! Para montar eso
voy a necesitar...
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<compilerId>groovy-eclipse-...
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<compilerId>groovy-eclipse-...
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<compilerId>groovy-eclipse-...
2. Dependencias con Spock2. Dependencias con Spock
<!-- Test dependencies -->
<dependency>
<groupId>org.codehaus.groovy</g...
3. Ejecutar tests *Spec (opc.)3. Ejecutar tests *Spec (opc.)
<!-- Surefire: include Spock tests (*Spec) -->
<plugin>
<grou...
3. Ejecutar tests *Spec (opc.)3. Ejecutar tests *Spec (opc.)
<!-- Surefire: include Spock tests (*Spec) -->
<plugin>
<grou...
Sólo dependenciasSólo dependencias
apply plugin: 'groovy'
// spock
testCompile 'org.codehaus.groovy:groovy-all:2.1.5'
test...
¿IDEs?¿IDEs?
Groovy Eclipse Plugin
– http://groovy.codehaus.org/Eclipse+Plugin
Versiones Eclipse entre 3.5 (Galileo) y 4.3...
¿Nada más?¿Nada más?
¡Nada más!
SDK Groovy no hace falta
Requisitos mínimos
JDK 5.0
Probado con Maven 2.0.9 (última 3.2.1....
Mi primer test SPOCKMi primer test SPOCK
import spock.lang.Specification;
class SillySpec extends Specification {
def "add two numbers"() {
expect:
1 + 1 == 2
}
}
...
import spock.lang.Specification;
class SillySpec extends Specification {
def "add two numbers"() {
expect:
1 + 1 == 2
}
}
...
import spock.lang.Specification;
class SillySpec extends Specification {
def "add two numbers"() {
expect:
1 + 1 == 2
}
}
...
El segundo test más tonto del mundoEl segundo test más tonto del mundo
def "add elements to a list"() {
given:
def list = ...
El segundo test más tonto del mundoEl segundo test más tonto del mundo
def "add elements to a list"() {
given:
def list = ...
El segundo test más tonto del mundoEl segundo test más tonto del mundo
def "add elements to a list"() {
given:
def list = ...
El segundo test más tonto del mundoEl segundo test más tonto del mundo
def "add elements to a list"() {
given:
def list = ...
El segundo test más tonto del mundoEl segundo test más tonto del mundo
def "add elements to a list"() {
given:
def list = ...
Organización en BloquesOrganización en Bloques
given (setup)
when
then
expect
where
cleanup
Estímulo /
respuesta
Comprobac...
Organización en BloquesOrganización en Bloques
given (setup)
when
then
expect
where
cleanup
Estímulo /
respuesta
Comprobac...
Condiciones then / expectCondiciones then / expect
when:
stack.push(elem)
then:
!stack.empty
stack.size() == 1
stack.peek(...
Ejecutando...Ejecutando...
Ejecutando...Ejecutando...
Ejecutando...Ejecutando...
Tests como documentaciónTests como documentación
@Issue("http://www.mybugtracking.com/BUG-012324")
def "add elements to a ...
Tests como documentaciónTests como documentación
@Issue("http://www.mybugtracking.com/BUG-012324")
def "add elements to a ...
Tests como documentaciónTests como documentación
@Issue("http://www.mybugtracking.com/BUG-012324")
def "add elements to a ...
Tests como documentaciónTests como documentación
@Issue("http://www.mybugtracking.com/BUG-012324")
def "add elements to a ...
Cambio de estadoCambio de estado
def "generate a sequential identifier"() {
given:
def gen = new SequentialIdGenerator()
w...
Cambio de estadoCambio de estado
def "generate a sequential identifier"() {
given:
def gen = new SequentialIdGenerator()
w...
Cambio de estadoCambio de estado
def "generate a sequential identifier"() {
given:
def gen = new SequentialIdGenerator()
w...
Matchers HamcrestMatchers Hamcrest
import static spock.util.matcher.HamcrestMatchers.closeTo
class HamcrestMatchers extend...
Control de la EjecuciónControl de la Ejecución
@Ignore
def "esta no se va a ejecutar"() { }
@Ignore(reason = "porque no fu...
Tests basados enTests basados en
DatosDatos
@Unroll
def "distance on #descrip"() {
expect:
LevenshteinCalculator.getLevenshteinDistance(s1, s2) == res
(res == 0? s1 =...
@Unroll
def "distance on #descrip"() {
expect:
LevenshteinCalculator.getLevenshteinDistance(s1, s2) == res
(res == 0? s1 =...
@Unroll
def "distance on #descrip"() {
expect:
LevenshteinCalculator.getLevenshteinDistance(s1, s2) == res
(res == 0? s1 =...
@Unroll
def "distance on #descrip"() {
expect:
LevenshteinCalculator.getLevenshteinDistance(s1, s2) == res
(res == 0? s1 =...
Pipes de datosPipes de datos
@Unroll
def "distance on #descrip"() {
expect:
LevenshteinCalculator.getLevenshteinDistance(s...
Pipes de datosPipes de datos
@Unroll
def "distance on #descrip"() {
expect:
LevenshteinCalculator.getLevenshteinDistance(s...
Pipes de datosPipes de datos
@Unroll
def "distance on #descrip"() {
expect:
LevenshteinCalculator.getLevenshteinDistance(s...
Pipes de datosPipes de datos
@Unroll
def "distance on #descrip"() {
expect:
LevenshteinCalculator.getLevenshteinDistance(s...
Pipes de datosPipes de datos
@Unroll
def "distance on #descrip"() {
expect:
LevenshteinCalculator.getLevenshteinDistance(s...
““Test doubles”Test doubles”
(mock objects)(mock objects)
¿Por qué “test doubles”?¿Por qué “test doubles”?
Problema: test de clase A que usa otra clase B que no
queremos probar:
Po...
StubsStubs
def "check valid comics"() {
given:
def apiStub = Stub(MarvelApi) {
findComicsByCharacter(_) >> [
new MarvelCom...
StubsStubs
def "check valid comics"() {
given:
def apiStub = Stub(MarvelApi) {
findComicsByCharacter(_) >> [
new MarvelCom...
StubsStubs
def "check valid comics"() {
given:
def apiStub = Stub(MarvelApi) {
findComicsByCharacter(_) >> [
new MarvelCom...
Stubs: constraintsStubs: constraints
Método: admite expresiones regulares
api./findComicsBy.*/(...)
Propiedad (getter)
api...
Stubs: comportamientoStubs: comportamiento
Siempre devolver mismo valor (>>)
stub.metodo(args) >> result1
Devolver valores...
Tests basados enTests basados en
InteraccionesInteracciones
External
Event Log
System
Questionnaire
DAO DB
Event Log
API
Questionnaire
Service
No hay resultado
que probar
Añadir unAñ...
External
Event Log
System
Questionnaire
DAO DB
Event Log
API
Questionnaire
Service
No hay resultado
que probar
Añadir unAñ...
Interacción con MocksInteracción con Mocks
def "add a questionnaire"() {
given: "a questionnaire with two questions"
def q...
Interacción con MocksInteracción con Mocks
def "add a questionnaire"() {
given: "a questionnaire with two questions"
def q...
Interacción con MocksInteracción con Mocks
def "add a questionnaire"() {
given: "a questionnaire with two questions"
def q...
Mocks en SpockMocks en Spock
Cardinalidad
Constraints son iguales que en Stubs
Mocking por defecto lenient (“indulgente”)
...
Shaken, not stirredShaken, not stirred
Interacciones se pueden mezclar con
condiciones de comprobación de datos
Mocks pued...
Shaken, not stirredShaken, not stirred
Interacciones se pueden mezclar con
condiciones de comprobación de datos
Mocks pued...
Recapitulemos...Recapitulemos...
¿¿¿Tests a prueba de Vagos???¿¿¿Tests a prueba de Vagos???
Subir nivel de abstracción
No “programar tests” → declarar caso...
¿¿¿Tests a prueba de Vagos???¿¿¿Tests a prueba de Vagos???
Subir nivel de abstracción
No “programar tests” → declarar caso...
¿¿¿Tests a prueba de Vagos???¿¿¿Tests a prueba de Vagos???
Subir nivel de abstracción
No “programar tests” → declarar caso...
¿¿¿Tests a prueba de Vagos???¿¿¿Tests a prueba de Vagos???
Subir nivel de abstracción
No “programar tests” → declarar caso...
¿¿¿Tests a prueba de Vagos???¿¿¿Tests a prueba de Vagos???
Subir nivel de abstracción
No “programar tests” → declarar caso...
¿Ibas a alguna parte?...
¡¡¡¿¿¿SOMOS HOMBRES¡¡¡¿¿¿SOMOS HOMBRES
O NENAZAS???!!!O NENAZAS???!!!
¿Ibas a alguna parte?...
Tests de integraciónTests de integración
Base de datos (objeto Sql)Base de datos (objeto Sql)
@Shared
@AutoCleanup("shutdown")
DataSource ds = new EmbeddedDatabase...
Base de datos (objeto Sql)Base de datos (objeto Sql)
@Shared
@AutoCleanup("shutdown")
DataSource ds = new EmbeddedDatabase...
Base de datos (objeto Sql)Base de datos (objeto Sql)
@Shared
@AutoCleanup("shutdown")
DataSource ds = new EmbeddedDatabase...
Base de datos (objeto Sql)Base de datos (objeto Sql)
def "find questionnaries" () {
final NAME = "Cuestionario de prueba"
...
Base de datos: DB UnitBase de datos: DB Unit
@Shared
@AutoCleanup("shutdown")
DataSource ds = new EmbeddedDatabaseBuilder(...
Base de datos: DB UnitBase de datos: DB Unit
@Shared
@AutoCleanup("shutdown")
DataSource ds = new EmbeddedDatabaseBuilder(...
SpringSpring
@ContextConfiguration(locations = "classpath:spring/application-config.xml")
class CourseRestControllerSpec e...
SpringSpring
@ContextConfiguration(locations = "classpath:spring/application-config.xml")
class CourseRestControllerSpec e...
TestsTests
FuncionalesFuncionales
(web)(web)
Tests web funcionalesTests web funcionales
class QuestionnariesPageSpec extends GebSpec {
def "questionnaries page check"(...
Tests web funcionalesTests web funcionales
class QuestionnariesPageSpec extends GebSpec {
def "questionnaries page check"(...
GebGeb
Very Groovy Browser Automation
Basado en Selenium
http://www.gebish.org/
Permite hacer capturas (reporting)
<depend...
Geb con Page ObjectsGeb con Page Objects
class PaginationModule extends Module {
def root
static content = {
paginationbar...
Geb con Page ObjectsGeb con Page Objects
class PaginationModule extends Module {
def root
static content = {
paginationbar...
Geb con Page ObjectsGeb con Page Objects
class PaginationModule extends Module {
def root
static content = {
paginationbar...
Geb con Page ObjectsGeb con Page Objects
class PaginationModule extends Module {
def root
static content = {
paginationbar...
Geb con Page ObjectsGeb con Page Objects
class QuestionnariesPageSpec extends GebSpec {
def "questionnaries page check"() ...
Tests deTests de
AceptaciónAceptación
Pruebas de aceptaciónPruebas de aceptación
@Title("Listado de cuestionarios")
@Narrative(""""
Como creador de juegos de cu...
Pruebas de aceptaciónPruebas de aceptación
@Title("Listado de cuestionarios")
@Narrative(""""
Como creador de juegos de cu...
Pruebas de aceptaciónPruebas de aceptación
@Title("Listado de cuestionarios")
@Narrative(""""
Como creador de juegos de cu...
Pruebas de aceptaciónPruebas de aceptación
@Title("Listado de cuestionarios")
@Narrative(""""
Como creador de juegos de cu...
Más informaciónMás información
Página principal Spock:
http://www.spockframework.org
Documentación:
http://docs.spockframe...
Más informaciónMás información
Geb
http://www.gebish.org/
spock-spring
http://code.google.com/p/spock/wiki/SpringExtension...
Gracias por la atención...Gracias por la atención...
Andrés ViedmaAndrés Viedma
@andres_viedma@andres_viedma
Prochain SlideShare
Chargement dans…5
×

Tests en Java con Groovy y Spock

3 285 vues

Publié le

No hay forma, por más que pruebas bibliotecas Java de tests no encuentras una forma de hacerlos sencilla y potente y que realmente te convenza del todo. Tu código de pruebas a menudo acaba siendo un pequeño batiburrillo ilegible que prefieres tocar lo mínimo posible, y que incluso te quita las ganas de hacer más tests. Has oído que la gente de Groovy habla muy bien de Spock, pero no te fías mucho de esos paganos que hacen guarreos con el código y no adoran debidamente al gran dios J.

En esta charla, que se presentó el 26/6/2014 organizada por MadridJUG y MadridGUG, Andrés Viedma nos mostrará lo sencillo que es integrar Spock en un proyecto Java, y cómo su gran expresividad y la potencia de Groovy nos pueden ayudar a crear tests en los que puedas preocuparte más de qué quieres probar que de cómo tienes que programar la prueba, y que además sirvan para documentar de una forma muy elegante cuál es el comportamiento esperado de nuestra querida aplicación Java. Se explorará además cómo Spock puede encajar perfectamente no solo con TDD sino también con la automatización de tests funcionales sobre la web, e incluso con técnicas de más alto nivel como BDD y metodologías ágiles en general.

Publié dans : Logiciels
  • DOWNLOAD THIS BOOKS INTO AVAILABLE FORMAT (Unlimited) ......................................................................................................................... ......................................................................................................................... Download Full PDF EBOOK here { https://soo.gd/qURD } ......................................................................................................................... Download Full EPUB Ebook here { https://soo.gd/qURD } ......................................................................................................................... Download Full doc Ebook here { https://soo.gd/qURD } ......................................................................................................................... Download PDF EBOOK here { https://soo.gd/qURD } ......................................................................................................................... Download EPUB Ebook here { https://soo.gd/qURD } ......................................................................................................................... Download doc Ebook here { https://soo.gd/qURD } ......................................................................................................................... ......................................................................................................................... ................................................................................................................................... eBook is an electronic version of a traditional print book THIS can be read by using a personal computer or by using an eBook reader. (An eBook reader can be a software application for use on a computer such as Microsoft's free Reader application, or a book-sized computer THIS is used solely as a reading device such as Nuvomedia's Rocket eBook.) Users can purchase an eBook on diskette or CD, but the most popular method of getting an eBook is to purchase a downloadable file of the eBook (or other reading material) from a Web site (such as Barnes and Noble) to be read from the user's computer or reading device. Generally, an eBook can be downloaded in five minutes or less ......................................................................................................................... .............. Browse by Genre Available eBooks .............................................................................................................................. Art, Biography, Business, Chick Lit, Children's, Christian, Classics, Comics, Contemporary, Cookbooks, Manga, Memoir, Music, Mystery, Non Fiction, Paranormal, Philosophy, Poetry, Psychology, Religion, Romance, Science, Science Fiction, Self Help, Suspense, Spirituality, Sports, Thriller, Travel, Young Adult, Crime, Ebooks, Fantasy, Fiction, Graphic Novels, Historical Fiction, History, Horror, Humor And Comedy, ......................................................................................................................... ......................................................................................................................... .....BEST SELLER FOR EBOOK RECOMMEND............................................................. ......................................................................................................................... Blowout: Corrupted Democracy, Rogue State Russia, and the Richest, Most Destructive Industry on Earth,-- The Ride of a Lifetime: Lessons Learned from 15 Years as CEO of the Walt Disney Company,-- Call Sign Chaos: Learning to Lead,-- StrengthsFinder 2.0,-- Stillness Is the Key,-- She Said: Breaking the Sexual Harassment Story THIS Helped Ignite a Movement,-- Atomic Habits: An Easy &amp; Proven Way to Build Good Habits &amp; Break Bad Ones,-- Everything Is Figureoutable,-- What It Takes: Lessons in the Pursuit of Excellence,-- Rich Dad Poor Dad: What the Rich Teach Their Kids About Money THIS the Poor and Middle Class Do Not!,-- The Total Money Makeover: Classic Edition: A Proven Plan for Financial Fitness,-- Shut Up and Listen!: Hard Business Truths THIS Will Help You Succeed, ......................................................................................................................... .........................................................................................................................
       Répondre 
    Voulez-vous vraiment ?  Oui  Non
    Votre message apparaîtra ici
  • DOWNLOAD THIS BOOKS INTO AVAILABLE FORMAT (2019 Update) ......................................................................................................................... ......................................................................................................................... Download Full PDF EBOOK here { https://soo.gd/irt2 } ......................................................................................................................... Download Full EPUB Ebook here { https://soo.gd/irt2 } ......................................................................................................................... Download Full doc Ebook here { https://soo.gd/irt2 } ......................................................................................................................... Download PDF EBOOK here { https://soo.gd/irt2 } ......................................................................................................................... Download EPUB Ebook here { https://soo.gd/irt2 } ......................................................................................................................... Download doc Ebook here { https://soo.gd/irt2 } ......................................................................................................................... ......................................................................................................................... ................................................................................................................................... eBook is an electronic version of a traditional print book THIS can be read by using a personal computer or by using an eBook reader. (An eBook reader can be a software application for use on a computer such as Microsoft's free Reader application, or a book-sized computer THIS is used solely as a reading device such as Nuvomedia's Rocket eBook.) Users can purchase an eBook on diskette or CD, but the most popular method of getting an eBook is to purchase a downloadable file of the eBook (or other reading material) from a Web site (such as Barnes and Noble) to be read from the user's computer or reading device. Generally, an eBook can be downloaded in five minutes or less ......................................................................................................................... .............. Browse by Genre Available eBooks .............................................................................................................................. Art, Biography, Business, Chick Lit, Children's, Christian, Classics, Comics, Contemporary, Cookbooks, Manga, Memoir, Music, Mystery, Non Fiction, Paranormal, Philosophy, Poetry, Psychology, Religion, Romance, Science, Science Fiction, Self Help, Suspense, Spirituality, Sports, Thriller, Travel, Young Adult, Crime, Ebooks, Fantasy, Fiction, Graphic Novels, Historical Fiction, History, Horror, Humor And Comedy, ......................................................................................................................... ......................................................................................................................... .....BEST SELLER FOR EBOOK RECOMMEND............................................................. ......................................................................................................................... Blowout: Corrupted Democracy, Rogue State Russia, and the Richest, Most Destructive Industry on Earth,-- The Ride of a Lifetime: Lessons Learned from 15 Years as CEO of the Walt Disney Company,-- Call Sign Chaos: Learning to Lead,-- StrengthsFinder 2.0,-- Stillness Is the Key,-- She Said: Breaking the Sexual Harassment Story THIS Helped Ignite a Movement,-- Atomic Habits: An Easy &amp; Proven Way to Build Good Habits &amp; Break Bad Ones,-- Everything Is Figureoutable,-- What It Takes: Lessons in the Pursuit of Excellence,-- Rich Dad Poor Dad: What the Rich Teach Their Kids About Money THIS the Poor and Middle Class Do Not!,-- The Total Money Makeover: Classic Edition: A Proven Plan for Financial Fitness,-- Shut Up and Listen!: Hard Business Truths THIS Will Help You Succeed, ......................................................................................................................... .........................................................................................................................
       Répondre 
    Voulez-vous vraiment ?  Oui  Non
    Votre message apparaîtra ici
  • DOWNLOAD THIS BOOKS INTO AVAILABLE FORMAT (2019 Update) ......................................................................................................................... ......................................................................................................................... Download Full PDF EBOOK here { https://soo.gd/irt2 } ......................................................................................................................... Download Full EPUB Ebook here { https://soo.gd/irt2 } ......................................................................................................................... Download Full doc Ebook here { https://soo.gd/irt2 } ......................................................................................................................... Download PDF EBOOK here { https://soo.gd/irt2 } ......................................................................................................................... Download EPUB Ebook here { https://soo.gd/irt2 } ......................................................................................................................... Download doc Ebook here { https://soo.gd/irt2 } ......................................................................................................................... ......................................................................................................................... ................................................................................................................................... eBook is an electronic version of a traditional print book THIS can be read by using a personal computer or by using an eBook reader. (An eBook reader can be a software application for use on a computer such as Microsoft's free Reader application, or a book-sized computer THIS is used solely as a reading device such as Nuvomedia's Rocket eBook.) Users can purchase an eBook on diskette or CD, but the most popular method of getting an eBook is to purchase a downloadable file of the eBook (or other reading material) from a Web site (such as Barnes and Noble) to be read from the user's computer or reading device. Generally, an eBook can be downloaded in five minutes or less ......................................................................................................................... .............. Browse by Genre Available eBooks .............................................................................................................................. Art, Biography, Business, Chick Lit, Children's, Christian, Classics, Comics, Contemporary, Cookbooks, Manga, Memoir, Music, Mystery, Non Fiction, Paranormal, Philosophy, Poetry, Psychology, Religion, Romance, Science, Science Fiction, Self Help, Suspense, Spirituality, Sports, Thriller, Travel, Young Adult, Crime, Ebooks, Fantasy, Fiction, Graphic Novels, Historical Fiction, History, Horror, Humor And Comedy, ......................................................................................................................... ......................................................................................................................... .....BEST SELLER FOR EBOOK RECOMMEND............................................................. ......................................................................................................................... Blowout: Corrupted Democracy, Rogue State Russia, and the Richest, Most Destructive Industry on Earth,-- The Ride of a Lifetime: Lessons Learned from 15 Years as CEO of the Walt Disney Company,-- Call Sign Chaos: Learning to Lead,-- StrengthsFinder 2.0,-- Stillness Is the Key,-- She Said: Breaking the Sexual Harassment Story THIS Helped Ignite a Movement,-- Atomic Habits: An Easy &amp; Proven Way to Build Good Habits &amp; Break Bad Ones,-- Everything Is Figureoutable,-- What It Takes: Lessons in the Pursuit of Excellence,-- Rich Dad Poor Dad: What the Rich Teach Their Kids About Money THIS the Poor and Middle Class Do Not!,-- The Total Money Makeover: Classic Edition: A Proven Plan for Financial Fitness,-- Shut Up and Listen!: Hard Business Truths THIS Will Help You Succeed, ......................................................................................................................... .........................................................................................................................
       Répondre 
    Voulez-vous vraiment ?  Oui  Non
    Votre message apparaîtra ici
  • DOWNLOAD THIS BOOKS INTO AVAILABLE FORMAT (2019 Update) ......................................................................................................................... ......................................................................................................................... Download Full PDF EBOOK here { https://soo.gd/irt2 } ......................................................................................................................... Download Full EPUB Ebook here { https://soo.gd/irt2 } ......................................................................................................................... Download Full doc Ebook here { https://soo.gd/irt2 } ......................................................................................................................... Download PDF EBOOK here { https://soo.gd/irt2 } ......................................................................................................................... Download EPUB Ebook here { https://soo.gd/irt2 } ......................................................................................................................... Download doc Ebook here { https://soo.gd/irt2 } ......................................................................................................................... ......................................................................................................................... ................................................................................................................................... eBook is an electronic version of a traditional print book THIS can be read by using a personal computer or by using an eBook reader. (An eBook reader can be a software application for use on a computer such as Microsoft's free Reader application, or a book-sized computer THIS is used solely as a reading device such as Nuvomedia's Rocket eBook.) Users can purchase an eBook on diskette or CD, but the most popular method of getting an eBook is to purchase a downloadable file of the eBook (or other reading material) from a Web site (such as Barnes and Noble) to be read from the user's computer or reading device. Generally, an eBook can be downloaded in five minutes or less ......................................................................................................................... .............. Browse by Genre Available eBooks .............................................................................................................................. Art, Biography, Business, Chick Lit, Children's, Christian, Classics, Comics, Contemporary, Cookbooks, Manga, Memoir, Music, Mystery, Non Fiction, Paranormal, Philosophy, Poetry, Psychology, Religion, Romance, Science, Science Fiction, Self Help, Suspense, Spirituality, Sports, Thriller, Travel, Young Adult, Crime, Ebooks, Fantasy, Fiction, Graphic Novels, Historical Fiction, History, Horror, Humor And Comedy, ......................................................................................................................... ......................................................................................................................... .....BEST SELLER FOR EBOOK RECOMMEND............................................................. ......................................................................................................................... Blowout: Corrupted Democracy, Rogue State Russia, and the Richest, Most Destructive Industry on Earth,-- The Ride of a Lifetime: Lessons Learned from 15 Years as CEO of the Walt Disney Company,-- Call Sign Chaos: Learning to Lead,-- StrengthsFinder 2.0,-- Stillness Is the Key,-- She Said: Breaking the Sexual Harassment Story THIS Helped Ignite a Movement,-- Atomic Habits: An Easy &amp; Proven Way to Build Good Habits &amp; Break Bad Ones,-- Everything Is Figureoutable,-- What It Takes: Lessons in the Pursuit of Excellence,-- Rich Dad Poor Dad: What the Rich Teach Their Kids About Money THIS the Poor and Middle Class Do Not!,-- The Total Money Makeover: Classic Edition: A Proven Plan for Financial Fitness,-- Shut Up and Listen!: Hard Business Truths THIS Will Help You Succeed, ......................................................................................................................... .........................................................................................................................
       Répondre 
    Voulez-vous vraiment ?  Oui  Non
    Votre message apparaîtra ici
  • DOWNLOAD THIS BOOKS INTO AVAILABLE FORMAT (Unlimited) ......................................................................................................................... ......................................................................................................................... Download Full PDF EBOOK here { https://soo.gd/qURD } ......................................................................................................................... Download Full EPUB Ebook here { https://soo.gd/qURD } ......................................................................................................................... Download Full doc Ebook here { https://soo.gd/qURD } ......................................................................................................................... Download PDF EBOOK here { https://soo.gd/qURD } ......................................................................................................................... Download EPUB Ebook here { https://soo.gd/qURD } ......................................................................................................................... Download doc Ebook here { https://soo.gd/qURD } ......................................................................................................................... ......................................................................................................................... ................................................................................................................................... eBook is an electronic version of a traditional print book THIS can be read by using a personal computer or by using an eBook reader. (An eBook reader can be a software application for use on a computer such as Microsoft's free Reader application, or a book-sized computer THIS is used solely as a reading device such as Nuvomedia's Rocket eBook.) Users can purchase an eBook on diskette or CD, but the most popular method of getting an eBook is to purchase a downloadable file of the eBook (or other reading material) from a Web site (such as Barnes and Noble) to be read from the user's computer or reading device. Generally, an eBook can be downloaded in five minutes or less ......................................................................................................................... .............. Browse by Genre Available eBooks .............................................................................................................................. Art, Biography, Business, Chick Lit, Children's, Christian, Classics, Comics, Contemporary, Cookbooks, Manga, Memoir, Music, Mystery, Non Fiction, Paranormal, Philosophy, Poetry, Psychology, Religion, Romance, Science, Science Fiction, Self Help, Suspense, Spirituality, Sports, Thriller, Travel, Young Adult, Crime, Ebooks, Fantasy, Fiction, Graphic Novels, Historical Fiction, History, Horror, Humor And Comedy, ......................................................................................................................... ......................................................................................................................... .....BEST SELLER FOR EBOOK RECOMMEND............................................................. ......................................................................................................................... Blowout: Corrupted Democracy, Rogue State Russia, and the Richest, Most Destructive Industry on Earth,-- The Ride of a Lifetime: Lessons Learned from 15 Years as CEO of the Walt Disney Company,-- Call Sign Chaos: Learning to Lead,-- StrengthsFinder 2.0,-- Stillness Is the Key,-- She Said: Breaking the Sexual Harassment Story THIS Helped Ignite a Movement,-- Atomic Habits: An Easy &amp; Proven Way to Build Good Habits &amp; Break Bad Ones,-- Everything Is Figureoutable,-- What It Takes: Lessons in the Pursuit of Excellence,-- Rich Dad Poor Dad: What the Rich Teach Their Kids About Money THIS the Poor and Middle Class Do Not!,-- The Total Money Makeover: Classic Edition: A Proven Plan for Financial Fitness,-- Shut Up and Listen!: Hard Business Truths THIS Will Help You Succeed, ......................................................................................................................... .........................................................................................................................
       Répondre 
    Voulez-vous vraiment ?  Oui  Non
    Votre message apparaîtra ici

Tests en Java con Groovy y Spock

  1. 1. SPOCKSPOCK Pruebas en Java con Groovy yPruebas en Java con Groovy y Andrés ViedmaAndrés Viedma
  2. 2. ¿Quién soy?¿Quién soy? Dinosaurio del software más de 20 años como profesional Javero inquieto Sospechoso habitual del MadridGUG y MadridJUG Escribo en Apaga y vuelve a encender http://apagayvuelveaencender.blogspot.com Andrés ViedmaAndrés Viedma @andres_viedma@andres_viedma
  3. 3. Pero... ¿de verdad hacemosPero... ¿de verdad hacemos pruebas?pruebas?
  4. 4. EL CASTIGADOR DE LOS TESTSEL CASTIGADOR DE LOS TESTS
  5. 5. EL CASTIGADOR DE LOS TESTSEL CASTIGADOR DE LOS TESTS Da su merecido (o sea, pruebas) a todas las líneas de código No hace excepciones
  6. 6. ROBIN HOOD, EL INFALIBLEROBIN HOOD, EL INFALIBLE
  7. 7. ROBIN HOOD, EL INFALIBLEROBIN HOOD, EL INFALIBLE Nunca falla un tiro. Ni tampoco falla en el código. Las pruebas son para los torpes
  8. 8. EL INCREIBLE PINOCHOEL INCREIBLE PINOCHO
  9. 9. EL INCREIBLE PINOCHOEL INCREIBLE PINOCHO Hace muchíiiiisimas pruebas. No se lo cree ni él.
  10. 10. EL INFORMÁTICO VAGOEL INFORMÁTICO VAGO
  11. 11. EL INFORMÁTICO VAGOEL INFORMÁTICO VAGO Hacer pruebas es importante.
  12. 12. EL INFORMÁTICO VAGOEL INFORMÁTICO VAGO Hacer pruebas es importante. Es una pena que también sea UN COÑAZO
  13. 13. ¿Y TÚ?...¿Y TÚ?...
  14. 14. Tests a prueba de VagosTests a prueba de Vagos Subir nivel de abstracción No “programar tests” → declarar casos de prueba Sencillez + potencia Expresividad → test es a la vez documentación Fácil de ejecutar en sistemas de integración continua y en IDEs Información que facilite la detección de errores
  15. 15. Mirando SPOCKMirando SPOCK
  16. 16. SPOCKSPOCK Hecho en Groovy
  17. 17. SPOCKSPOCK Hecho en Groovy
  18. 18. SPOCKSPOCK Muy parecido a Java (“extensión” del lenguaje) Compatible con él (se ejecuta en JVM) Lenguaje dinámico (o no) Mucho “azúcar sintáctico” Mucha “magia negra” Diseñado para maximizar sencillez y expresividad Tiene su propio runner JUnit Hecho en Groovy
  19. 19. ¡Uf! Para montar eso voy a necesitar...
  20. 20. ¡Uf! Para montar eso voy a necesitar...
  21. 21. <plugin> <artifactId>maven-compiler-plugin</artifactId> <version>3.1</version> <configuration> <compilerId>groovy-eclipse-compiler</compilerId> </configuration> <dependencies> <dependency> <groupId>org.codehaus.groovy</groupId> <artifactId>groovy-eclipse-compiler</artifactId> <version>2.8.0-01</version> </dependency> <dependency> <groupId>org.codehaus.groovy</groupId> <artifactId>groovy-eclipse-batch</artifactId> <version>2.1.8-01</version> </dependency> </dependencies> </plugin> 1. Compilar código Groovy1. Compilar código Groovy
  22. 22. <plugin> <artifactId>maven-compiler-plugin</artifactId> <version>3.1</version> <configuration> <compilerId>groovy-eclipse-compiler</compilerId> </configuration> <dependencies> <dependency> <groupId>org.codehaus.groovy</groupId> <artifactId>groovy-eclipse-compiler</artifactId> <version>2.8.0-01</version> </dependency> <dependency> <groupId>org.codehaus.groovy</groupId> <artifactId>groovy-eclipse-batch</artifactId> <version>2.1.8-01</version> </dependency> </dependencies> </plugin> 1. Compilar código Groovy1. Compilar código Groovy
  23. 23. <plugin> <artifactId>maven-compiler-plugin</artifactId> <version>3.1</version> <configuration> <compilerId>groovy-eclipse-compiler</compilerId> </configuration> <dependencies> <dependency> <groupId>org.codehaus.groovy</groupId> <artifactId>groovy-eclipse-compiler</artifactId> <version>2.8.0-01</version> </dependency> <dependency> <groupId>org.codehaus.groovy</groupId> <artifactId>groovy-eclipse-batch</artifactId> <version>2.1.8-01</version> </dependency> </dependencies> </plugin> 1. Compilar código Groovy1. Compilar código Groovy
  24. 24. 2. Dependencias con Spock2. Dependencias con Spock <!-- Test dependencies --> <dependency> <groupId>org.codehaus.groovy</groupId> <artifactId>groovy-all</artifactId> <version>2.1.5</version> <scope>test</scope> </dependency> <dependency> <groupId>org.spockframework</groupId> <artifactId>spock-core</artifactId> <version>0.7-groovy-2.0</version> <scope>test</scope> </dependency>
  25. 25. 3. Ejecutar tests *Spec (opc.)3. Ejecutar tests *Spec (opc.) <!-- Surefire: include Spock tests (*Spec) --> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <version>2.14</version> <configuration> <includes> <include>**/*Spec.java</include> <include>**/Test*.java</include> <include>**/*Test.java</include> <include>**/*TestCase.java</include> </includes> </configuration> </plugin>
  26. 26. 3. Ejecutar tests *Spec (opc.)3. Ejecutar tests *Spec (opc.) <!-- Surefire: include Spock tests (*Spec) --> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <version>2.14</version> <configuration> <includes> <include>**/*Spec.java</include> <include>**/Test*.java</include> <include>**/*Test.java</include> <include>**/*TestCase.java</include> </includes> </configuration> </plugin>
  27. 27. Sólo dependenciasSólo dependencias apply plugin: 'groovy' // spock testCompile 'org.codehaus.groovy:groovy-all:2.1.5' testCompile( group:'org.spockframework',name:'spock-core', version:'0.7-groovy-2.0')
  28. 28. ¿IDEs?¿IDEs? Groovy Eclipse Plugin – http://groovy.codehaus.org/Eclipse+Plugin Versiones Eclipse entre 3.5 (Galileo) y 4.3 (Kepler) Instalar versión adecuada (Extra Groovy compilers – 2.1) Plugin Groovy incluido en instalación
  29. 29. ¿Nada más?¿Nada más? ¡Nada más! SDK Groovy no hace falta Requisitos mínimos JDK 5.0 Probado con Maven 2.0.9 (última 3.2.1...) Eclipse Galileo No requiere cambios importantes en entorno de desarrollo
  30. 30. Mi primer test SPOCKMi primer test SPOCK
  31. 31. import spock.lang.Specification; class SillySpec extends Specification { def "add two numbers"() { expect: 1 + 1 == 2 } } El test más tonto del mundoEl test más tonto del mundo src/test/groovy/SillySpec.groovy
  32. 32. import spock.lang.Specification; class SillySpec extends Specification { def "add two numbers"() { expect: 1 + 1 == 2 } } El test más tonto del mundoEl test más tonto del mundo src/test/groovy/SillySpec.groovy
  33. 33. import spock.lang.Specification; class SillySpec extends Specification { def "add two numbers"() { expect: 1 + 1 == 2 } } El test más tonto del mundoEl test más tonto del mundo “Assert” implícito src/test/groovy/SillySpec.groovy
  34. 34. El segundo test más tonto del mundoEl segundo test más tonto del mundo def "add elements to a list"() { given: def list = ["one", "two"] when: list.add("three") list << “four” then: list == ["one", "two", "three", "four"] }
  35. 35. El segundo test más tonto del mundoEl segundo test más tonto del mundo def "add elements to a list"() { given: def list = ["one", "two"] when: list.add("three") list << “four” then: list == ["one", "two", "three", "four"] }
  36. 36. El segundo test más tonto del mundoEl segundo test más tonto del mundo def "add elements to a list"() { given: def list = ["one", "two"] when: list.add("three") list << “four” then: list == ["one", "two", "three", "four"] }
  37. 37. El segundo test más tonto del mundoEl segundo test más tonto del mundo def "add elements to a list"() { given: def list = ["one", "two"] when: list.add("three") list << “four” then: list == ["one", "two", "three", "four"] } DSL
  38. 38. El segundo test más tonto del mundoEl segundo test más tonto del mundo def "add elements to a list"() { given: def list = ["one", "two"] when: list.add("three") list << “four” then: list == ["one", "two", "three", "four"] } equals Tipos opcionales Collection literals ; opcional
  39. 39. Organización en BloquesOrganización en Bloques given (setup) when then expect where cleanup Estímulo / respuesta Comprobación directa and: encadenar varios bloques del mismo tipo
  40. 40. Organización en BloquesOrganización en Bloques given (setup) when then expect where cleanup Estímulo / respuesta Comprobación directa Legibilidad When/then: efectos laterales Expect: método funcional puro and: encadenar varios bloques del mismo tipo
  41. 41. Condiciones then / expectCondiciones then / expect when: stack.push(elem) then: !stack.empty stack.size() == 1 stack.peek() == elem Condiciones booleanas sencillas when: stack.pop() then: thrown(EmptyStackException) stack.empty Condiciones excepciones thrown / notThrown Interacciones (...) Sólo pueden contener condiciones o definición de variables
  42. 42. Ejecutando...Ejecutando...
  43. 43. Ejecutando...Ejecutando...
  44. 44. Ejecutando...Ejecutando...
  45. 45. Tests como documentaciónTests como documentación @Issue("http://www.mybugtracking.com/BUG-012324") def "add elements to a list"() { given: "a list with elements” def list = ["one", "two"] when: "two more are added” list.add("three") list << “four” then: "the list includes now both elements” list == ["one", "two", "three", "four"] }
  46. 46. Tests como documentaciónTests como documentación @Issue("http://www.mybugtracking.com/BUG-012324") def "add elements to a list"() { given: "a list with elements” def list = ["one", "two"] when: "two more are added” list.add("three") list << “four” then: "the list includes now both elements” list == ["one", "two", "three", "four"] }
  47. 47. Tests como documentaciónTests como documentación @Issue("http://www.mybugtracking.com/BUG-012324") def "add elements to a list"() { given: "a list with elements” def list = ["one", "two"] when: "two more are added” list.add("three") list << “four” then: "the list includes now both elements” list == ["one", "two", "three", "four"] } Comportamiento queda mejor documentado
  48. 48. Tests como documentaciónTests como documentación @Issue("http://www.mybugtracking.com/BUG-012324") def "add elements to a list"() { given: "a list with elements” def list = ["one", "two"] when: "two more are added” list.add("three") list << “four” then: "the list includes now both elements” list == ["one", "two", "three", "four"] } Comportamiento queda mejor documentado Bueno para razonamiento TDD
  49. 49. Cambio de estadoCambio de estado def "generate a sequential identifier"() { given: def gen = new SequentialIdGenerator() when: def id = gen.generateId() then: id == old(gen.nextId) gen.nextId == old(gen.nextId) + 1 }
  50. 50. Cambio de estadoCambio de estado def "generate a sequential identifier"() { given: def gen = new SequentialIdGenerator() when: def id = gen.generateId() then: id == old(gen.nextId) gen.nextId == old(gen.nextId) + 1 }
  51. 51. Cambio de estadoCambio de estado def "generate a sequential identifier"() { given: def gen = new SequentialIdGenerator() when: def id = gen.generateId() then: id == old(gen.nextId) gen.nextId == old(gen.nextId) + 1 } Ojo: no usar si el resultado es un objeto mutable
  52. 52. Matchers HamcrestMatchers Hamcrest import static spock.util.matcher.HamcrestMatchers.closeTo class HamcrestMatchers extends Specification { def "comparing two decimal numbers"() { def myPi = 3.14 expect: myPi closeTo(Math.PI, 0.01) } }
  53. 53. Control de la EjecuciónControl de la Ejecución @Ignore def "esta no se va a ejecutar"() { } @Ignore(reason = "porque no funciona ni p'atrás") def "esta tampoco se va a ejecutar"() { } @IgnoreRest def "si lo pongo esta va a ser la única en ejecutarse"() { } @IgnoreIf({ os.windows }) def "esta solo se ejecutaría en Windows"() { } @Stepwise class RunInOrderSpec extends Specification { def "Este será siempre el primero"() { ... } def "Este se ejecutará el segundo"() { ... } } @Timeout(5) def "Falla si tarda más de 5 segundos"() { } Ejecución selectiva Timeout Orden de ejecución
  54. 54. Tests basados enTests basados en DatosDatos
  55. 55. @Unroll def "distance on #descrip"() { expect: LevenshteinCalculator.getLevenshteinDistance(s1, s2) == res (res == 0? s1 == s2 : s1 != s2) where: s1 | s2 | descrip || res "pepito" | "pepito" | "same values" || 0 "pepito" | "pePito" | "only case difference" || 1 "pepito" | "qerida" | "many char differences" || 4 "pepito" | "p" | "shorter value" || 5 "p" | "otro" | "larger value" || 4 "12345" | "6" | "all different" || 5 "" | "1234" | "empty and non empty" || 4 "" | "" | "both empty" || 0 "12 34" | "12 34" | "differences in spaces" || 1 "one vision"| "one visn" | "two chars in the middle" || 2 } Tablas de datosTablas de datos
  56. 56. @Unroll def "distance on #descrip"() { expect: LevenshteinCalculator.getLevenshteinDistance(s1, s2) == res (res == 0? s1 == s2 : s1 != s2) where: s1 | s2 | descrip || res "pepito" | "pepito" | "same values" || 0 "pepito" | "pePito" | "only case difference" || 1 "pepito" | "qerida" | "many char differences" || 4 "pepito" | "p" | "shorter value" || 5 "p" | "otro" | "larger value" || 4 "12345" | "6" | "all different" || 5 "" | "1234" | "empty and non empty" || 4 "" | "" | "both empty" || 0 "12 34" | "12 34" | "differences in spaces" || 1 "one vision"| "one visn" | "two chars in the middle" || 2 } Tablas de datosTablas de datos
  57. 57. @Unroll def "distance on #descrip"() { expect: LevenshteinCalculator.getLevenshteinDistance(s1, s2) == res (res == 0? s1 == s2 : s1 != s2) where: s1 | s2 | descrip || res "pepito" | "pepito" | "same values" || 0 "pepito" | "pePito" | "only case difference" || 1 "pepito" | "qerida" | "many char differences" || 4 "pepito" | "p" | "shorter value" || 5 "p" | "otro" | "larger value" || 4 "12345" | "6" | "all different" || 5 "" | "1234" | "empty and non empty" || 4 "" | "" | "both empty" || 0 "12 34" | "12 34" | "differences in spaces" || 1 "one vision"| "one visn" | "two chars in the middle" || 2 } Tablas de datosTablas de datos
  58. 58. @Unroll def "distance on #descrip"() { expect: LevenshteinCalculator.getLevenshteinDistance(s1, s2) == res (res == 0? s1 == s2 : s1 != s2) where: s1 | s2 | descrip || res "pepito" | "pepito" | "same values" || 0 "pepito" | "pePito" | "only case difference" || 1 "pepito" | "qerida" | "many char differences" || 4 "pepito" | "p" | "shorter value" || 5 "p" | "otro" | "larger value" || 4 "12345" | "6" | "all different" || 5 "" | "1234" | "empty and non empty" || 4 "" | "" | "both empty" || 0 "12 34" | "12 34" | "differences in spaces" || 1 "one vision"| "one visn" | "two chars in the middle" || 2 } Tablas de datosTablas de datos Tests diferenciados
  59. 59. Pipes de datosPipes de datos @Unroll def "distance on #descrip"() { expect: LevenshteinCalculator.getLevenshteinDistance(s1, s2) == res (res == 0? s1 == s2 : s1 != s2) where: s1 << ["pepito", "pepito", "pepito", "pepito", "p"] s2 << ["pepito", "pePito", "qerida", "p", "otro"] descrip << ["same values", "only case difference", "many char differences", "shorter value", "larger value"] res << [0, 1, 4, 5, 4] }
  60. 60. Pipes de datosPipes de datos @Unroll def "distance on #descrip"() { expect: LevenshteinCalculator.getLevenshteinDistance(s1, s2) == res (res == 0? s1 == s2 : s1 != s2) where: s1 << ["pepito", "pepito", "pepito", "pepito", "p"] s2 << ["pepito", "pePito", "qerida", "p", "otro"] descrip << ["same values", "only case difference", "many char differences", "shorter value", "larger value"] res << [0, 1, 4, 5, 4] }
  61. 61. Pipes de datosPipes de datos @Unroll def "distance on #descrip"() { expect: LevenshteinCalculator.getLevenshteinDistance(s1, s2) == res (res == 0? s1 == s2 : s1 != s2) where: [s1, s2, descrip, resStr] << new File("testdata.csv").readLines() .collect {line -> line.tokenize(",")} res = Integer.parseInt(resStr) }
  62. 62. Pipes de datosPipes de datos @Unroll def "distance on #descrip"() { expect: LevenshteinCalculator.getLevenshteinDistance(s1, s2) == res (res == 0? s1 == s2 : s1 != s2) where: [s1, s2, descrip, resStr] << new File("testdata.csv").readLines() .collect {line -> line.tokenize(",")} res = Integer.parseInt(resStr) }
  63. 63. Pipes de datosPipes de datos @Unroll def "distance on #descrip"() { expect: LevenshteinCalculator.getLevenshteinDistance(s1, s2) == res (res == 0? s1 == s2 : s1 != s2) where: [s1, s2, descrip, resStr] << new File("testdata.csv").readLines() .collect {line -> line.tokenize(",")} res = Integer.parseInt(resStr) } Asignaciones de variables
  64. 64. ““Test doubles”Test doubles” (mock objects)(mock objects)
  65. 65. ¿Por qué “test doubles”?¿Por qué “test doubles”? Problema: test de clase A que usa otra clase B que no queremos probar: Porque utiliza recursos externos (BD, APIs externas...) Para independizar las pruebas “Test doubles” reemplazan la clase B por objetos “de pega” Stub: devuelve respuestas prefijadas en el test Mock: cascarón vacío con respuestas por defecto Spy: pone una capa sobre un objeto real para espiar las llamadas que se le hacen
  66. 66. StubsStubs def "check valid comics"() { given: def apiStub = Stub(MarvelApi) { findComicsByCharacter(_) >> [ new MarvelComic(id: 1, date: new Date(), creators: [new ComicCreator(id: 101)] ), (............) new MarvelComic(id: 6, date: null, creators: [new ComicCreator(id: 103)] ) ] } MarvelQuestionnaireFactory f = new MarvelQuestionFactory(apiStub) expect: f.loadValidQuestionnarieComics(1)*.id == [1, 5] }
  67. 67. StubsStubs def "check valid comics"() { given: def apiStub = Stub(MarvelApi) { findComicsByCharacter(_) >> [ new MarvelComic(id: 1, date: new Date(), creators: [new ComicCreator(id: 101)] ), (............) new MarvelComic(id: 6, date: null, creators: [new ComicCreator(id: 103)] ) ] } MarvelQuestionnaireFactory f = new MarvelQuestionFactory(apiStub) expect: f.loadValidQuestionnarieComics(1)*.id == [1, 5] } Stub de una clase añadir dependencias a cglib-nodep y objenesis
  68. 68. StubsStubs def "check valid comics"() { given: def apiStub = Stub(MarvelApi) { findComicsByCharacter(_) >> [ new MarvelComic(id: 1, date: new Date(), creators: [new ComicCreator(id: 101)] ), (............) new MarvelComic(id: 6, date: null, creators: [new ComicCreator(id: 103)] ) ] } MarvelQuestionnaireFactory f = new MarvelQuestionFactory(apiStub) expect: f.loadValidQuestionnarieComics(1)*.id == [1, 5] } Named parameter constructor
  69. 69. Stubs: constraintsStubs: constraints Método: admite expresiones regulares api./findComicsBy.*/(...) Propiedad (getter) api.apiKey Argumentos stub.metodo("hello") stub.metodo(!"hello") stub.metodo() stub.metodo(_) stub.metodo(*_) stub.metodo(_ as String) stub.metodo({ l -> l.size() > 3 })
  70. 70. Stubs: comportamientoStubs: comportamiento Siempre devolver mismo valor (>>) stub.metodo(args) >> result1 Devolver valores secuencialmente (>>>) stub.metodo(args) >>> [res1, res2, res3] Ejecución de código (cambio estado, calcular retorno) stub.metodo(...) >> { args -> ..... } stub.metodo(...) >> { arg -> ..... } Encadenar llamadas de distinto tipo stub.metodo(args) >>> [r1, r2] >> { (code) } >> r4 Llamada no declarada: valor por defecto / objeto vacío (no null)
  71. 71. Tests basados enTests basados en InteraccionesInteracciones
  72. 72. External Event Log System Questionnaire DAO DB Event Log API Questionnaire Service No hay resultado que probar Añadir unAñadir un cuestionariocuestionario Tests de Interacciones: por quéTests de Interacciones: por qué
  73. 73. External Event Log System Questionnaire DAO DB Event Log API Questionnaire Service No hay resultado que probar Añadir unAñadir un cuestionariocuestionario Tests de Interacciones: por quéTests de Interacciones: por qué ¡¡¡NO LO PROBAM OS!!!
  74. 74. Interacción con MocksInteracción con Mocks def "add a questionnaire"() { given: "a questionnaire with two questions" def q = new Questionnaire() q.addQuestion(new Question()) q.addQuestion(new Question()) and: "a service with mocked collaborators" def dao = Mock(QuestionnaireDao) def eventLog = Mock(EventLogApi) def service = new QuestionnaireService(dao, eventLog) when: "the questionnaire is created" service.addQuestionnaire(q) then: "the questionnaire + questions are created, the event logged" 1 * dao.addQuestionnaireBean(_) 2 * dao.addQuestionBean(_) 1 * eventLog.registerEvent { ev -> ev.type == EventType.ADD_QUESTIONNAIRE } }
  75. 75. Interacción con MocksInteracción con Mocks def "add a questionnaire"() { given: "a questionnaire with two questions" def q = new Questionnaire() q.addQuestion(new Question()) q.addQuestion(new Question()) and: "a service with mocked collaborators" def dao = Mock(QuestionnaireDao) def eventLog = Mock(EventLogApi) def service = new QuestionnaireService(dao, eventLog) when: "the questionnaire is created" service.addQuestionnaire(q) then: "the questionnaire + questions are created, the event logged" 1 * dao.addQuestionnaireBean(_) 2 * dao.addQuestionBean(_) 1 * eventLog.registerEvent { ev -> ev.type == EventType.ADD_QUESTIONNAIRE } }
  76. 76. Interacción con MocksInteracción con Mocks def "add a questionnaire"() { given: "a questionnaire with two questions" def q = new Questionnaire() q.addQuestion(new Question()) q.addQuestion(new Question()) and: "a service with mocked collaborators" def dao = Mock(QuestionnaireDao) def eventLog = Mock(EventLogApi) def service = new QuestionnaireService(dao, eventLog) when: "the questionnaire is created" service.addQuestionnaire(q) then: "the questionnaire + questions are created, the event logged" 1 * dao.addQuestionnaireBean(_) 2 * dao.addQuestionBean(_) 1 * eventLog.registerEvent { ev -> ev.type == EventType.ADD_QUESTIONNAIRE } } Interacción = Cardinalidad * Constraint
  77. 77. Mocks en SpockMocks en Spock Cardinalidad Constraints son iguales que en Stubs Mocking por defecto lenient (“indulgente”) Estricto - añadir al final regla: 0 * _ Orden de llamadas no se considera Para hacerlo, poner cada comprobación en un bloque “then” diferenciado 1 * subscriber.receive("hello") 0 * subscriber.receive("hello") (1..3) * subscriber.receive("hello") (1.._) * subscriber.receive("hello") (_..3) * subscriber.receive("hello") _ * subscriber.receive("hello")
  78. 78. Shaken, not stirredShaken, not stirred Interacciones se pueden mezclar con condiciones de comprobación de datos Mocks pueden tener métodos stubbeados Valores por defecto distintos a Stub: 0 / false / null Spies: wrapper sobre implementación de clase real Se pueden chequear interacciones Se pueden stubbear métodos
  79. 79. Shaken, not stirredShaken, not stirred Interacciones se pueden mezclar con condiciones de comprobación de datos Mocks pueden tener métodos stubbeados Valores por defecto distintos a Stub: 0 / false / null Spies: wrapper sobre implementación de clase real Se pueden chequear interacciones Se pueden stubbear métodos
  80. 80. Recapitulemos...Recapitulemos...
  81. 81. ¿¿¿Tests a prueba de Vagos???¿¿¿Tests a prueba de Vagos??? Subir nivel de abstracción No “programar tests” → declarar casos de prueba Sencillez + potencia Expresividad → test es a la vez documentación Fácil de ejecutar en sistemas de integración continua y en IDEs Información que facilite la detección de errores
  82. 82. ¿¿¿Tests a prueba de Vagos???¿¿¿Tests a prueba de Vagos??? Subir nivel de abstracción No “programar tests” → declarar casos de prueba Sencillez + potencia Expresividad → test es a la vez documentación Fácil de ejecutar en sistemas de integración continua y en IDEs Información que facilite la detección de errores
  83. 83. ¿¿¿Tests a prueba de Vagos???¿¿¿Tests a prueba de Vagos??? Subir nivel de abstracción No “programar tests” → declarar casos de prueba Sencillez + potencia Expresividad → test es a la vez documentación Fácil de ejecutar en sistemas de integración continua y en IDEs Información que facilite la detección de errores
  84. 84. ¿¿¿Tests a prueba de Vagos???¿¿¿Tests a prueba de Vagos??? Subir nivel de abstracción No “programar tests” → declarar casos de prueba Sencillez + potencia Expresividad → test es a la vez documentación Fácil de ejecutar en sistemas de integración continua y en IDEs Información que facilite la detección de errores
  85. 85. ¿¿¿Tests a prueba de Vagos???¿¿¿Tests a prueba de Vagos??? Subir nivel de abstracción No “programar tests” → declarar casos de prueba Sencillez + potencia Expresividad → test es a la vez documentación Fácil de ejecutar en sistemas de integración continua y en IDEs Información que facilite la detección de errores ¡¡¡YESSSSSSSSSSSSS!!!¡¡¡YESSSSSSSSSSSSS!!!
  86. 86. ¿Ibas a alguna parte?...
  87. 87. ¡¡¡¿¿¿SOMOS HOMBRES¡¡¡¿¿¿SOMOS HOMBRES O NENAZAS???!!!O NENAZAS???!!! ¿Ibas a alguna parte?...
  88. 88. Tests de integraciónTests de integración
  89. 89. Base de datos (objeto Sql)Base de datos (objeto Sql) @Shared @AutoCleanup("shutdown") DataSource ds = new EmbeddedDatabaseBuilder().setType(EmbeddedDatabaseType.HSQL).build(); @Shared Sql sql = Sql.newInstance(ds) @Shared SqlSession session @Shared @Subject QuestionnarieDao dao def setupSpec() { // DDL sql.execute(''' create table questionnaries ( id bigint not null identity, name varchar(200) not null ); ''') // MyBatis config / DAO creation def transactionFactory = new JdbcTransactionFactory(); def environment = new Environment("development", transactionFactory, ds); def configuration = new Configuration(environment); configuration.addMapper(QuestionnarieDao.class); def builder = new SqlSessionFactoryBuilder(); def factory = builder.build(configuration); session = factory.openSession() dao = session.getMapper(QuestionnarieDao.class) }
  90. 90. Base de datos (objeto Sql)Base de datos (objeto Sql) @Shared @AutoCleanup("shutdown") DataSource ds = new EmbeddedDatabaseBuilder().setType(EmbeddedDatabaseType.HSQL).build(); @Shared Sql sql = Sql.newInstance(ds) @Shared SqlSession session @Shared @Subject QuestionnarieDao dao def setupSpec() { // DDL sql.execute(''' create table questionnaries ( id bigint not null identity, name varchar(200) not null ); ''') // MyBatis config / DAO creation def transactionFactory = new JdbcTransactionFactory(); def environment = new Environment("development", transactionFactory, ds); def configuration = new Configuration(environment); configuration.addMapper(QuestionnarieDao.class); def builder = new SqlSessionFactoryBuilder(); def factory = builder.build(configuration); session = factory.openSession() dao = session.getMapper(QuestionnarieDao.class) }
  91. 91. Base de datos (objeto Sql)Base de datos (objeto Sql) @Shared @AutoCleanup("shutdown") DataSource ds = new EmbeddedDatabaseBuilder().setType(EmbeddedDatabaseType.HSQL).build(); @Shared Sql sql = Sql.newInstance(ds) @Shared SqlSession session @Shared @Subject QuestionnarieDao dao def setupSpec() { // DDL sql.execute(''' create table questionnaries ( id bigint not null identity, name varchar(200) not null ); ''') // MyBatis config / DAO creation def transactionFactory = new JdbcTransactionFactory(); def environment = new Environment("development", transactionFactory, ds); def configuration = new Configuration(environment); configuration.addMapper(QuestionnarieDao.class); def builder = new SqlSessionFactoryBuilder(); def factory = builder.build(configuration); session = factory.openSession() dao = session.getMapper(QuestionnarieDao.class) }
  92. 92. Base de datos (objeto Sql)Base de datos (objeto Sql) def "find questionnaries" () { final NAME = "Cuestionario de prueba" given: sql.execute("insert into questionnaries(name) values (${NAME})") sql.commit() when: def qlist = dao.findActiveQuestionnaries() then: qlist.size() == 1 qlist[0].name == NAME } def "insert questionnarie" () { final NAME = "Cuestionario nuevo" when: dao.insertQuestionnarie(new Questionnarie([name: NAME])) session.commit() then: sql.firstRow("select * from questionnaries where name = ${NAME}").id != null and: sql.rows("select * from questionnaries").size() == old(sql.rows("select * from questionnaries").size()) + 1 }
  93. 93. Base de datos: DB UnitBase de datos: DB Unit @Shared @AutoCleanup("shutdown") DataSource ds = new EmbeddedDatabaseBuilder().setType(EmbeddedDatabaseType.HSQL).build() (...) @DbUnit def dbState = { Questionnaries(id: 1, name: 'Cuestionario de prueba') Questionnaries(id: 2, name: 'Otro cuestionario') Questionnaries(id: 3, name: 'Y otro más') } (...) def "find questionnaries" () { when: def qlist = dao.findActiveQuestionnaries() then: qlist.size() == 3 qlist[0].name == "Cuestionario de prueba" }
  94. 94. Base de datos: DB UnitBase de datos: DB Unit @Shared @AutoCleanup("shutdown") DataSource ds = new EmbeddedDatabaseBuilder().setType(EmbeddedDatabaseType.HSQL).build() (...) @DbUnit def dbState = { Questionnaries(id: 1, name: 'Cuestionario de prueba') Questionnaries(id: 2, name: 'Otro cuestionario') Questionnaries(id: 3, name: 'Y otro más') } (...) def "find questionnaries" () { when: def qlist = dao.findActiveQuestionnaries() then: qlist.size() == 3 qlist[0].name == "Cuestionario de prueba" } spock-dbunit
  95. 95. SpringSpring @ContextConfiguration(locations = "classpath:spring/application-config.xml") class CourseRestControllerSpec extends Specification { @Autowired @Subject CourseRestController controller def "get courses"() { when: ListPage<Course> courses = controller.getCourses(new PaginationDesc(from: 1, max: 10)) then: courses.listSize == 7 courses.elements[2].title == "Intensivo de rueda cubana" } }
  96. 96. SpringSpring @ContextConfiguration(locations = "classpath:spring/application-config.xml") class CourseRestControllerSpec extends Specification { @Autowired @Subject CourseRestController controller def "get courses"() { when: ListPage<Course> courses = controller.getCourses(new PaginationDesc(from: 1, max: 10)) then: courses.listSize == 7 courses.elements[2].title == "Intensivo de rueda cubana" } } spock-spring
  97. 97. TestsTests FuncionalesFuncionales (web)(web)
  98. 98. Tests web funcionalesTests web funcionales class QuestionnariesPageSpec extends GebSpec { def "questionnaries page check"() { final EXPECTED_ELEMENT = "Cuestionario chulo" given: go "/es/questionnaries" expect: $("p.recordcount > .valor").text() == "7" and: def link = $("ol.pag-registros > li .media-heading a")[5] link.text() == EXPECTED_ELEMENT when: link.click() then: title == EXPECTED_ELEMENT } }
  99. 99. Tests web funcionalesTests web funcionales class QuestionnariesPageSpec extends GebSpec { def "questionnaries page check"() { final EXPECTED_ELEMENT = "Cuestionario chulo" given: go "/es/questionnaries" expect: $("p.recordcount > .valor").text() == "7" and: def link = $("ol.pag-registros > li .media-heading a")[5] link.text() == EXPECTED_ELEMENT when: link.click() then: title == EXPECTED_ELEMENT } }
  100. 100. GebGeb Very Groovy Browser Automation Basado en Selenium http://www.gebish.org/ Permite hacer capturas (reporting) <dependency> <groupId>org.gebish</groupId> <artifactId>geb-spock</artifactId> <version>0.9.2</version> <scope>test</scope> </dependency> <dependency> <groupId>org.seleniumhq.selenium</groupId> <artifactId>selenium-htmlunit-driver</artifactId> <version>2.26.0</version> <scope>test</scope> </dependency> <dependency> <groupId>org.seleniumhq.selenium</groupId> <artifactId>selenium-firefox-driver</artifactId> <version>2.26.0</version> <scope>test</scope> </dependency> Dependencias Configuracioń: /GebConfig.groovy (DSL) import org.openqa.selenium.htmlunit.HtmlUnitDriver; driver = { new HtmlUnitDriver() } baseUrl = "http://xxxxxxxxxxxxxxxxx" Instalar Drivers (PhantomJS, Firefox...)
  101. 101. Geb con Page ObjectsGeb con Page Objects class PaginationModule extends Module { def root static content = { paginationbar { root.find(".paginationbar") } total { paginationbar.find (".recordcount .valor").text() as int } pageElements { root.find("ol.pag-registros > li") } } } class QuestionnarieHeader extends Module { def root static content = { link { root.find(".media-heading a") } description { link.text() } } } class QuestionnariesListPage extends Page { static url = "/es/questionnaries" static at = { title == "Registros" } static content = { pagination { module PaginationModule, root: $(".sumario_registros .pagination-container") } questionnaries { pagination.pageElements.collect { module QuestionnarieHeader, root: it } } } }
  102. 102. Geb con Page ObjectsGeb con Page Objects class PaginationModule extends Module { def root static content = { paginationbar { root.find(".paginationbar") } total { paginationbar.find (".recordcount .valor").text() as int } pageElements { root.find("ol.pag-registros > li") } } } class QuestionnarieHeader extends Module { def root static content = { link { root.find(".media-heading a") } description { link.text() } } } class QuestionnariesListPage extends Page { static url = "/es/questionnaries" static at = { title == "Registros" } static content = { pagination { module PaginationModule, root: $(".sumario_registros .pagination-container") } questionnaries { pagination.pageElements.collect { module QuestionnarieHeader, root: it } } } } Page object - url: para ir a la página - at: para comprobar si estamos en ella - content: acceso rápido a elementos
  103. 103. Geb con Page ObjectsGeb con Page Objects class PaginationModule extends Module { def root static content = { paginationbar { root.find(".paginationbar") } total { paginationbar.find (".recordcount .valor").text() as int } pageElements { root.find("ol.pag-registros > li") } } } class QuestionnarieHeader extends Module { def root static content = { link { root.find(".media-heading a") } description { link.text() } } } class QuestionnariesListPage extends Page { static url = "/es/questionnaries" static at = { title == "Registros" } static content = { pagination { module PaginationModule, root: $(".sumario_registros .pagination-container") } questionnaries { pagination.pageElements.collect { module QuestionnarieHeader, root: it } } } } Module object Elemento reutilizable por varias páginas
  104. 104. Geb con Page ObjectsGeb con Page Objects class PaginationModule extends Module { def root static content = { paginationbar { root.find(".paginationbar") } total { paginationbar.find (".recordcount .valor").text() as int } pageElements { root.find("ol.pag-registros > li") } } } class QuestionnarieHeader extends Module { def root static content = { link { root.find(".media-heading a") } description { link.text() } } } class QuestionnariesListPage extends Page { static url = "/es/questionnaries" static at = { title == "Registros" } static content = { pagination { module PaginationModule, root: $(".sumario_registros .pagination-container") } questionnaries { pagination.pageElements.collect { module QuestionnarieHeader, root: it } } } } Forma de usar los módulos dentro de un page object
  105. 105. Geb con Page ObjectsGeb con Page Objects class QuestionnariesPageSpec extends GebSpec { def "questionnaries page check"() { final EXPECTED_ELEMENT = "Cuestionario chulo" given: to QuestionnariesListPage expect: at QuestionnariesListPage and: pagination.total == 7 and: def quest = questionnaries[5] quest.description == EXPECTED_ELEMENT when: quest.link.click() then: waitFor { at QuestionnariePage } questionnarieTitle == EXPECTED_ELEMENT } }
  106. 106. Tests deTests de AceptaciónAceptación
  107. 107. Pruebas de aceptaciónPruebas de aceptación @Title("Listado de cuestionarios") @Narrative("""" Como creador de juegos de cuestionarios quiero poder consultar la lista de cuestionarios ya existentes para poder crear un nuevo cuestionario basado en otro anterior """) class QuestionnariesPageSpec extends GebSpec { def "scenario: comprobación listado"() { final EXPECTED_ELEMENT = "Cuestionario chulo" given: "Estamos en la lista de cuestionarios" to QuestionnariesListPage expect: "Que la página sea la correcta" at QuestionnariesListPage and: "El número de elementos sea el correcto" pagination.total == 222 and: "Se comprueba que uno de los elementos sea el correcto" def quest = questionnaries[5] quest.description == EXPECTED_ELEMENT when: "Se clica en él" quest.link.click() then: "Se comprueba que se va a su ficha y que el título sea el correcto" waitFor { at QuestionnariePage } questionnarieTitle == EXPECTED_ELEMENT } } Historia de usuario
  108. 108. Pruebas de aceptaciónPruebas de aceptación @Title("Listado de cuestionarios") @Narrative("""" Como creador de juegos de cuestionarios quiero poder consultar la lista de cuestionarios ya existentes para poder crear un nuevo cuestionario basado en otro anterior """) class QuestionnariesPageSpec extends GebSpec { def "scenario: comprobación listado"() { final EXPECTED_ELEMENT = "Cuestionario chulo" given: "Estamos en la lista de cuestionarios" to QuestionnariesListPage expect: "Que la página sea la correcta" at QuestionnariesListPage and: "El número de elementos sea el correcto" pagination.total == 222 and: "Se comprueba que uno de los elementos sea el correcto" def quest = questionnaries[5] quest.description == EXPECTED_ELEMENT when: "Se clica en él" quest.link.click() then: "Se comprueba que se va a su ficha y que el título sea el correcto" waitFor { at QuestionnariePage } questionnarieTitle == EXPECTED_ELEMENT } } Criterios de aceptación
  109. 109. Pruebas de aceptaciónPruebas de aceptación @Title("Listado de cuestionarios") @Narrative("""" Como creador de juegos de cuestionarios quiero poder consultar la lista de cuestionarios ya existentes para poder crear un nuevo cuestionario basado en otro anterior """) class QuestionnariesPageSpec extends GebSpec { def "scenario: comprobación listado"() { final EXPECTED_ELEMENT = "Cuestionario chulo" given: "Estamos en la lista de cuestionarios" to QuestionnariesListPage expect: "Que la página sea la correcta" at QuestionnariesListPage and: "El número de elementos sea el correcto" pagination.total == 222 and: "Se comprueba que uno de los elementos sea el correcto" def quest = questionnaries[5] quest.description == EXPECTED_ELEMENT when: "Se clica en él" quest.link.click() then: "Se comprueba que se va a su ficha y que el título sea el correcto" waitFor { at QuestionnariePage } questionnarieTitle == EXPECTED_ELEMENT } } Cooperación cliente, UX, front, back...
  110. 110. Pruebas de aceptaciónPruebas de aceptación @Title("Listado de cuestionarios") @Narrative("""" Como creador de juegos de cuestionarios quiero poder consultar la lista de cuestionarios ya existentes para poder crear un nuevo cuestionario basado en otro anterior """) class QuestionnariesPageSpec extends GebSpec { def "scenario: comprobación listado"() { final EXPECTED_ELEMENT = "Cuestionario chulo" given: "Estamos en la lista de cuestionarios" to QuestionnariesListPage expect: "Que la página sea la correcta" at QuestionnariesListPage and: "El número de elementos sea el correcto" pagination.total == 222 and: "Se comprueba que uno de los elementos sea el correcto" def quest = questionnaries[5] quest.description == EXPECTED_ELEMENT when: "Se clica en él" quest.link.click() then: "Se comprueba que se va a su ficha y que el título sea el correcto" waitFor { at QuestionnariePage } questionnarieTitle == EXPECTED_ELEMENT } } BDD Behaviour Driven Development Cooperación cliente, UX, front, back...
  111. 111. Más informaciónMás información Página principal Spock: http://www.spockframework.org Documentación: http://docs.spockframework.org/ Documentación antigua: http://code.google.com/p/spock/w/list Spock Web Console http://meet.spockframework.org/ Proyecto de ejemplo http://files.spockframework.org/spock-example-0.5-groovy-1.7.zip Lenguaje Groovy http://beta.groovy-lang.org/docs/groovy-2.3.1/html/documentation/#_lists Modificaciones Groovy a librería estándar JDK http://groovy.codehaus.org/groovy-jdk/
  112. 112. Más informaciónMás información Geb http://www.gebish.org/ spock-spring http://code.google.com/p/spock/wiki/SpringExtension spock-dbunit https://github.com/janbols/spock-dbunit
  113. 113. Gracias por la atención...Gracias por la atención... Andrés ViedmaAndrés Viedma @andres_viedma@andres_viedma

×