Después de la primera parte de Testing orientado a BDD con Spock, en el que vimos cómo escribir especificaciones BDD para pruebas con Spock, en este post vamos a ver algunas utilidades más avanzadas que tenemos a nuestra disposición: cómo hacer mocking y stubbing, cómo testear excepciones o cómo utilizar algunas extensiones que nos pueden ser de gran ayuda.

También veremos cómo sacar unos estupendos informes de tests que nos serán útiles tanto para el equipo como para el cliente.

Comenzamos…

Mocking y Stubbing

Seguro que, después de leer el primer post, te has preguntado cómo simular el comportamiento de algunos de tus servicios o funcionalidades sin necesidad de implementarlos o ejecutarlos. Para ello, Spock nos da soporte para hacer mocking y stubbing. Veamos en qué consiste cada uno.

Cuando hacemos ‘mocking’ conseguimos simular el comportamiento de una clase y las interacciones con otros colaboradores.

Cuando hacemos ‘stubbing’ podemos especificar cómo queremos que se comporten los métodos de una clase.

Si vemos el ejemplo, al hacer mocking de ‘NotificacionService’ lo estamos simulando, sin necesidad de levantar el contexto de la aplicación. Por otro lado, al hacer stubbing de ‘CustomerRepositoy’ no solo lo estamos simulando, sino que estamos especificando qué resultado queremos que devuelva su método ‘save’ (en esta caso, que devuelva ‘true’) sin especificar qué parámetros le vamos a pasar.

Por tanto, en este escenario, el resultado de llamar al método ‘CustomerService.registerCustomer()’ con unos datos cualquiera es que se va a invocar al método ‘NotificationService.sendWelcomeMessage()’ devolviendo el mensaje ‘Hi, welcome!’.

groovy

interface NotificationService {
   def sendWelcomeMessage(Customer customer, String message);
   def sendErrorMessage(Customer customer, String message);
}

NotificationService.groovy

@RepositoryRestResource(collectionResourceRel = "customer", path = "customer")
public interface CustomerRepository extends CrudRepository<Customer, Long> {
   List<Customer> findByName(String name)
   // Method save --> implicit method because
   // CustomerRepository extends of CrudRepository
}

CustomerRepository.groovy

void 'Send welcome notificacion when customer is created'() {
   setup:
       // Mocking
       def mockedNotificationService = Mock(NotificationService)
       // Stubbing
       def stubbedCustomerRepository = Stub(CustomerRepository){
           save(_) >> true // customerRepository.save() method was ok
       }
       def customerService = new CustomerService(mockedNotificationService,stubbedCustomerRepository)
   When: 'A costumer is created correctly'
       customerService.registerCustomer('CustomerName', 'CustomerSurname')
   then: 'A welcome notification is sent'
       1 * mockedNotificationService.sendWelcomeMessage(_, "Hi, welcome!") // One call to this method
    and: 'An error message is not sent'
       0 * mockedNotificationService.sendErrorMessage(_, _) // No calls to this method
}

MockingAndStubbingSpec.groovy

class CustomerService {
   private NotificationService notificationService
   private CustomerRepository customerRepository
   public CustomerService(NotificationService notificationService,CustomerRepository customerRepository) {
       this.notificationService = notificationService
       this.customerRepository = customerRepository
   }
   void registerCustomer(String name, String lastName) {
       Customer customer = new Customer(name:name, lastName:lastName)
       if(customerRepository.save(customer))
           notificationService.sendWelcomeMessage(customer, "Hi, welcome!")
       else {
           notificationService.sendErrorMessage(customer, "Oops, an error has ocurred!")
       }
   }
}

CustomerService.groovy

Exceptions

Con Spock también podemos comprobar en nuestros test si se lanzan excepciones:


def 'Throw NullPointerException'() {
   given: 'a null object'
       Customer customer = null
   when: 'try to access to the object'
       customer.name
   then:'throw a nullpointerexception'
       thrown NullPointerException
}

Extensions

Además de anotaciones como las ya vistas en el post anteior (@Shared, @Unroll), veamos ahora algunas otras disponibles que nos proporcionarán utilidades extra a la hora de implementar nuestros tests.

@Narrative & @Title

Podemos utilizar estas anotaciones para añadir un título y una explicación a nuestros tests, como por ejemplo, la descripción de la historia de usuario.


@Narrative("""
As a user
I want to search customers by name
to find them quickly
""")
@Title("""This is easy
to read
_______________________
""")
class Example9_SearchCustomerUnitSpec extends Specification { … }

@Issue

Podemos utilizar esta extensión para enlazar la ‘issue’, ‘tarea’, ‘user story’ o elemento al que haga referencia nuestro test.

groovy

""")
@Issue("https://github.com/spockframework/spock/issues/684")
class Example9_SearchCustomerUnitSpec extends Specification{...}

@Ignore, @IgnoreRest & @IgnoreIf

Estas etiquetas nos sirven respectivamente para: ignorar un test, ignorar el resto (menos el anotado) o ignorar bajo una condición:

groovy

@IgnoreIf({ isFriendsCharacter("Rachel") })
void 'run if is a friend character'() {
   expect:
       true
}
// helper methods
static boolean isFriendsCharacter(String name) {
   name in ['Rachel', 'Monica', 'Phoebe', 'Joey', 'Chandler', 'Ross']
}

@Requires

Cuando necesitamos que se cumpla una condición:

groovy

@Requires({ OperatingSystem.current.macOs })
void 'only run on MacOS'() {
   expect:
       println 'MacOS'
       true
}

@Stepwise

Una clase de test anotada con @Stepwise asegura que el orden de ejecución de los test es tal y como se ha declarado. Además, si uno de los tests de la clase falla, el resto no se ejecuta. Si tenemos tests anotados con alguno de los @Ignore… se ignorarán tal y como se hayan declarado.

@Timeout

Sirve para que los tests de una clase o un test en concreto fallen si se excede de un tiempo determinado (por defecto en milisegundos aunque podemos especificar las unidades):

groovy

@Timeout(value = 100, unit = TimeUnit.MILLISECONDS)

Encontramos más información en ‘Extensions’.

Informes de tests

Como introducía al principio de este post, Spock, por defecto, nos proporciona unos informes en html o xml donde, por cada ejecución, se muestra el resultado de dichos tests. Pero, además, está disponible la librería ‘Spock Reports Extension’ que da un paso más en la generación de dichos informes, proporcionando más información y permitiendo customización.

Si, por ejemplo, usas Gradle, solo tienes que añadir esta librería a tus dependencias:

groovy

testCompile( 'com.athaydes:spock-reports:1.2.13' )

Como resultado, tendremos unos informes con este aspecto:

Resumen de tests
Resumen de tests
Resultado con éxito
Resultado con éxito
Datatable - Uso de @Unroll con sustitución de variables - resultado fallido
Datatable - Uso de @Unroll con sustitución de variables - resultado fallido
Datatable - Uso de @Unroll con sustitución de variables - resultado fallido
Datatable - Uso de @Unroll con sustitución de variables - resultado fallido

Por último, puedes encontrar toda la documentación y recursos del framework en la web oficial de Spock. Espero que después de haber leído el primer post y ahora éste, te animes a implementar las pruebas de tus proyectos con este framework y que aproveches la ocasión para iniciarte con Groovy como lenguaje de programación.

Cuéntanos qué te parece.

Los comentarios serán moderados. Serán visibles si aportan un argumento constructivo. Si no estás de acuerdo con algún punto, por favor, muestra tus opiniones de manera educada.

Suscríbete

Estamos comprometidos.

Tecnología, personas e impacto positivo.