Está en la página 1de 21

Guía patrones de automatización

Introducción

Un patrón es una solución general reutilizable para un problema común en un contexto dado. En
ingeniería de software, un patrón es una técnica de diseño de software que se utiliza para dar
solución a una clase de problema concreto.

¿Qué no es un patrón?

• Prescriptivo, es decir, un patrón no establece como se deben hacer las cosas, este puede
ser mutable ante las condiciones del problema y la experticia del desarrollador.
• Un procedimiento paso a paso. Un patrón no es una receta que se deba seguir
estrictamente al pie de la letra.
• Una solución terminada que puede simplemente adaptarse al problema que se tiene. Un
patrón no siempre es aplicable, se debe analizar, si el patrón que se pretende aplicar
genera valor a mi desarrollo.

¿Dónde utilizarlos?

• Sólo en el caso de tener el mismo problema que soluciona el patrón.


• En un caso particular puede no ser aplicable.
• Abusar del uso de los patrones es un error muy común.

Ventajas

• Proporcionan elementos reusables en el diseño.


• Efectividad comprobada en la resolución de problemas similares.
• Formalizan un vocabulario común entre diseñadores.
• Estandarizan el diseño.

Desventajas

• El uso de un patrón no se refleja claramente en el código.


• Sobrecarga de trabajo a la hora de implementar.

Tipos de patrones

• Según su propósito

- Creacionales
- De comportamiento

• Según su alcance

- De clase
- De objeto
Patron Builder

• Permite la creación de una variedad de objetos complejos desde un objeto fuente.


• Se clasifica como un patrón creacional.
• Abstrae el proceso de creación de un objeto complejo, centralizando dicho proceso en un
único punto.

Ventajas

• Reduce el acoplamiento.
• Se independiza el código de construcción de la representación.
• Permite un mayor control en el proceso de creación del objeto.

Diagrama de clases

En la figura 1 se puede observar el diagrama de clases que se utiliza para el patrón builder.
A continuación, se da una breve explicación de la función que cumple cada uno de sus
elementos:

• Builder:
Interfaz abstracta para crear productos.

• Concrete Builder:
- Implementación del Builder
- Construye y reúne las partes necesarias para construir los productos

• Director:
Construye un objeto usando el patrón Builder.

• Producto:
El objeto complejo bajo construcción

Figura 1. Diagrama de clases patrón builder.


Ejercicio Práctico

Aplicar el patrón builder a la siguiente clase producto:

public class Task {


private final long id;
private String summary = "";
private String description = "";
private boolean done = false;
private Date dueDate;

public Task(long id) {


this.id = id;
}

public Task(long id, String summary, String description,


boolean done,
Date dueDate) {
this.id = id;
this.summary = summary;
this.description = description;
this.done = done;
this.dueDate = dueDate;

public long getId() {


return id;
}

public String getSummary() {


return summary;
}

public void setSummary(String summary) {


this.summary = summary;
}

public String getDescription() {


return description;
}

public void setDescription(String description) {


this.description = description;
}
public boolean isDone() {
return done;
}

public void setDone(boolean done) {


this.done = done;
}

public Date getDueDate() {


return new Date(dueDate.getTime());
}

public void setDueDate(Date dueDate) {


this.dueDate = new Date(dueDate.getTime());
}
}

En la figura 1 se puede observar como debería ser el modelo para implementar el patrón. Dado
que no es necesario totalmente pegarse a la teoría del patrón nosotros los modelaremos de la
siguiente manera:

public class TaskBuilder {

private final long id;


private String summary = "";
private String description = "";
private boolean done = false;
private Date dueDate;

public TaskBuilder(long id, String summary, String


description, boolean done,
Date dueDate) {
this.id = id;
this.summary = summary;
this.description = description;
this.done = done;
this.dueDate = dueDate;
}

public TaskBuilder setSummary(String summary) {


this.summary = summary;
return this;
}

public TaskBuilder setDescription(String description) {


this.description = description;
return this;
}

public TaskBuilder setDone(boolean done) {


this.done = done;
return this;
}

public TaskBuilder setDueDate(Date dueDate) {


this.dueDate = new Date(dueDate.getTime());
return this;
}
public Task build() {
return new Task(id,summary, description,done, dueDate);
}
}

Y su implementación quedaría de la siguiente manera:

public class MainTest {

public static void main(String[] args) {


Task task = new
TaskBuilder(5).setDescription("Hello").setSummary("Test").build(
);
System.out.println(task);
}

Sin embargo, en un proyecto se puede tener varios objetos builder, y se puede garantizar que
como mínimo todos tendrán una función build, por ende, se realizará una refactorización del
código para obligar a todas las clases builder a que como mínimo deben implementar dicho
método. Para dicha refactorización, como primer paso, se creará una interfaz con un método
build como se observa a continuación:

public interface Builder <T>{

T build();

Luego se debe refactorizar la clase que implementa el build, en este caso la clase TaskBuilder,
como se observa a continuación:
public class TaskBuilder implements Builder<Task> {

private final long id;


private String summary = "";
private String description = "";
private boolean done = false;
private Date dueDate;

public TaskBuilder(long id, String summary, String


description, boolean done,
Date dueDate) {
this.id = id;
this.summary = summary;
this.description = description;
this.done = done;
this.dueDate = dueDate;
}

public TaskBuilder setSummary(String summary) {


this.summary = summary;
return this;
}

public TaskBuilder setDescription(String description) {


this.description = description;
return this;
}

public TaskBuilder setDone(boolean done) {


this.done = done;
return this;
}

public TaskBuilder setDueDate(Date dueDate) {


this.dueDate = new Date(dueDate.getTime());
return this;
}

@Override
public Task build() {
return new Task(id,summary, description,done, dueDate);
}

}
Screen Play
Screenplay se presenta como una mejora al patrón Page Object principalmente porque usando
page object, el automatizador construye código basado en la interfaz de usuario y no en las
interacciones que tiene el usuario con el sistema. Este patrón, centrado en el usuario y
orientado a tareas es la aplicación de los principios S.O.L.I.D en los proyectos de
automatización, especialmente el principio de responsabilidad única y abierto-cerrado.
Este patrón habilita al automatizador a diseñar dirigido en el dominio (DDD), lo que se espera se
traduzca en un mayor acercamiento al negocio dado que siempre se habla y desarrolla en
términos de negocio (lenguaje ubiquo). Para mayor información acerca de DDD puede dirigirse
al libro de Erick Evans: Domain-Driven Design: Tackling Complexity in the Heart of Software o al
siguiente enlace donde se resume este enfoque.

Implementar correctamente screenplay permitirá además, escribir código en un lenguaje mucho


más cercano al lenguaje natural, esto es reproducido en los reportes, por tanto se resalta la
importancia de esto. Un ejemplo presente en la documentación oficial sobre la legibilidad
esperada en el código se muestra en la figura 2.

Figura 2. Ejemplo de alta legibilidad del código

La importancia de la alta legibilidad del código facilita principalmente la mantenibilidad y la


reusabilidad dentro del proyecto, debido a que se favorece enormemente la descripción de lo
que el sistema hace y lo que se está tratando de probar. Se resalta que esto hace parte de
la documentación de las pruebas automatizadas, de hecho, es la documentación más valiosa
de las mismas.

Screenplay es “framework agnostic”, eso quiere decir que no tiene dependencias con la
tecnología, procesos o sistemas, sin embargo, para Bancolombia será aplicado
usando Serenity BDD.

Usar screenplay obliga a pensar en términos de roles, objetivos y acciones para lograr construir
software limpio, con buenas prácticas y que cumpla con los requisitos no funcionales de
usabilidad y mantenibilidad mínimamente. Aquello que se automatiza está en términos del
proceso de negocio y no en cómo llegar a el (clicks y selects). El modelo se presenta en la figura
3:
Figura 3. Modelo conceptual de screenplay

¿Cómo leer ese modelo? : Un usuario (DOÑA CLARA), habilitado para abrir el navegador ejecuta
una serie de tareas (procesos de negocio como transferir saldo) a través de una serie de acciones
directamente relacionadas con la aplicación (como ingresando a la aplicación, consultando el
saldo, ingresando la cuenta destino y el valor a transferir) y realiza un conjunto de preguntas
acerca del estado de esa aplicación (como consultar que se haya realizado el débito a su
cuenta)

Considere un escenario como el que se observa en la figura 4. Descomponiendo mentalmente


ese escenario(ver figura 5), se encuentra que ese usuario está habilitado o intenta llegar a un fin
(then) a través de la ejecución de tareas (tasks) y acciones (actions)
Figura 4. Escenario de ejemplo

Figura 5. Descomposición en tareas y acciones para el escenario de ejemplo

A nivel de código fuente, tomando el ejemplo de la figura 2, se tiene que siempre que se aplique
el patrón se logrará tener un código legible en términos de negocio y no de cómo funciona la
aplicación a través de clics. A esto se le llama “Layers of abstraction”.
Figura 6. Patrón screenplay aplicado

Capas de la arquitectura:

En la figura 7 se presentan las capas que conformarán la arquitectura de referencia de los


proyectos de automatización del área de certificación. Cada capa tiene una responsabilidad
específica:

• Features:

Son las narrativas o .feature que equivalen a los escenarios de cucumber diseñados
aplicando BDD. Esos archivos son nombrados en minúscula, separados por “_” y pueden
contener uno o más escenarios dependiendo del caso de negocio de
prueba. Ejm: filtering_client_bank_accounts.feature

Se espera que los escenarios sean especificados usando lenguaje Gherkin y que estén
dados en términos de lo que el usuario espera que pase más no del cómo debe hacerlo.
Es decir, escribir los escenarios como un proceso de negocio de alto nivel, no en términos
de clicks y selects. En la figura 8 se muestra un ejemplo aclaratorio.

Más información la puede obtener en la siguiente documentación:

▪ A BIT OF UCD FOR BDD & ATDD: GOALS -> TASKS -> ACTIONS
▪ Browser Automation - Serenity
▪ Serenity with Cucumber

Algo muy importante y un caso muy frecuente es el de reuso de escenarios, para esto
se propone no reusar a nivel de narrativas sino de tareas. Para hacer una mayor
aclaración por favor diríjase a la siguiente documentación:

How not to prepare test data in JBehave and Cucumber


Figura 7. Arquitectura de referencia

Figura 8. Malas y buenas prácticas cuando se construye un escenario

Adicional a esto representan los runners o archivos que serán ejecutados


por Serenity cuando corran la pruebas. Estos archivos mapean a las narrativas y por
cuestiones de legibilidad y mantenibilidad se aconseja que vayan nombrados de la misma
manera en la que se nombraron las narrativas a las que mapean pero en Camel case.
Para el ejemplo anterior el runner se nombraría así: FilteringClientBankAccounts.java
Un ejemplo de un runner se presenta en la figura 9:

Figura 9. Clase ejecutable de suite de pruebas.

• Step Definitions:

Son clases que mapean cada línea de los escenarios definidos en lenguaje Gherkin a
métodos java. Por cuestiones de legibilidad y mantenibilidad se aconseja que vayan
nombrados de la misma manera en la que se nombraron los runner
agregando StepDefinitions al
final. Ejm: FilteringClientBankAccountsStepDefinitions.java

Un ejemplo de un step definition se presenta en la figura 10:

Figura 10. Ejemplo de step definition.

En el @Before se especifican las habilidades (abilities) del actor, lo que en términos


técnicos indica que las habilidades representan el arrange de una prueba o la preparación
de la misma. Dentro de cada mapeo de líneas Gherkin, se hace referencia a las tareas
(tasks) no a las acciones (actions) dado que las tareas tal y como se especifica en el
modelo de la figura 3 están en un nivel de abstracción más alto que las acciones.

El uso de expresiones regulares es importante cuando se quiere establecer un reuso de


sentencias que varían en parámetros o algunas palabras. Por tanto, se recomienda su
uso.
• Tasks:

Son tareas a un nivel de granularidad más alto al de clicks y selects. Representan


operaciones importantes para llegar a cumplir una meta (lo que se está
probando). Ejm consultar saldo, hacer transferencia, completar un pago, matricular
cuentas. Las tareas, al ser operaciones, son verbos y así mismo serán nombrados,
además implementan de Task. Es necesario aquí, aplicar el principio de única
responsabilidad, es decir, una tarea es representada por una clase, una clase no debe
ser usada para representar 2 o más tareas. Una tareas puede tener uno o más métodos
nombrados basados en el patrón objectBuilder para facilitar la legibilidad de
los tests cuando éstos sean llamados. Una tarea puede estar compuesta por otras tareas
o por acciones. Un ejemplo de una tarea se presenta en la figura 11:

Figura 11. Ejemplo de una tarea.

Cuando una tarea es compuesta por otras tareas o por acciones, en el reporte, ese paso
a paso se verá reflejado.

Figura 12. Los reportes muestran las relaciones entre tareas y acciones.
• Interactions:

Una interacción representa una acción directa del usuario con la interfaz como ingresar
datos en campos o dar clics en botones. Serenity screenplay trae por defecto muchas
interacciones sobre la interfaz, por tanto, no siempre es necesario crear nuevas
interacciones. Un ejemplo de una interacción se representa en la figura 13:

Figura 13. Ejemplo de una acción.

• Questions:

Es la capa donde se especifica el assert de las pruebas. Donde se verifican los resultados
de las operaciones realizadas en las capas anteriores. Serenity usa una librería que se
llama Hamcrest para la construcción de assertions fluidos, esta librería trae muchos por
defecto, en caso de que haya un assertion que sea muy específico del negocio entonces
se crearía en la capa questions. Un ejemplo se presenta en la figura 14:

Figura 14. Ejemplo de una clase question.

Figura 15. Clase que servirá como Factory para crear Questions de objetos de negocio llamados Items.
Figura 16. Uso del question.

Es válido el uso de patrones para mantener la legibilidad y simplicidad de las oraciones


dentro de los steps definitions.

• User interface:

Son las clases que mapean los componentes de una interfaz de usuario. Sólo deben
realizar ese mapeo, no tienen comportamiento. Se nombran basados en el contenedor
de lo que se está mapeando en mayúscula separado por guión bajo. Ejemplo:

Figura 17. Ejemplo del mapeo de una interfaz de usuario.

• Integration:
Una capa opcional que será creada a criterio de cada equipo en caso de que hayan
integraciones a otros subsistemas. Para conocer acerca del funcionamiento de la misma
y un ejemplo por favor diríjase al apartado “Recomendaciones para trabajar con
componentes o puntos de control que no tengan interfaz de usuario” de este mismo
documento.

• Capas transversales o de uso general en el proyecto:

Util:

Una capa opcional que será creada a criterio de cada equipo en caso de que hayan
utilidades que consideren se puedan reusar.

Model:

Una capa donde se encontrarán todos los objetos complejos de negocio, como personas
o cuentas bancarias para ser usados dentro de cualquier capa del proyecto.
Exceptions:
Una capa donde se crearán las excepciones específicas que permitirán la legibilidad de
los reportes cuando las pruebas fallen. La razón de ser de esta capa es porque
normalmente las excepciones del web driver son ambiguas y generalizadas, esto hace
que no entreguen mucha información acerca del estado real de la aplicación en el
momento de la ejecución de los test. Un correcto manejo de las excepciones asegurará
un reporte con errores específicos y bien descritos (no técnicos) para ser presentado
como documentación ante entidades reguladoras.

El estándar de nombramiento de las excepciones es con el tipo de excepción terminando


con la palabra Exception. Ejm: ClientSearchException

Dentro de los tipos de excepciones, se categorizan específicamente tres:

1. Fallas (failures): Indican que la aplicación no se está comportando como es esperado.


Excepciones de tipo AssertionError son categorizados como fallas. Se presentan en color
rojo en la documentación viva.

2. Errores (errors): Indican un error potencial de la implementación del test en si mismo.


Normalmente las excepciones del webDriver se categorizan como errores. Se presentan en
color naranja en la documentación viva.

Excepciones como ElementNotVisibleException son excepciones del webDriver que


serán tomadas como errores. Ese tipo de excepciones no siempre son errores, pueden
ser lanzadas como resultado de una falla en la aplicación. En este caso, para que esa
excepción sea lanzada como falla, es necesario construir una excepción propia de
dominio que extienda de una que si sea una falla, como por ejemplo de AssertionError y
controlarla en los posibles puntos en los que pueda ser lanzada. Eso además de mejorar
los mensajes de error que genera Serenity tran las ejecución de las pruebas, también
permitirá a las automatizaciones ser más asertivas en términos del tipo de error lanzado.

3. Comprometidos (compromised): Indican que una excepción particular presentada


durante la ejecución de la prueba está relacionada a un problema con la estabilidad del
ambiente o algo que vaya más allá del alcance de los tests. Se presentan en color púrpura
en la documentación viva. Para identificar que tipos de excepciones generan que las
pruebas queden con estado comprometido, se debe agregar al serenity.conf una
propiedad con las excepciones, de la siguiente manera:

serenity.compromised.on=org.openqa.selenium.NoSuchFrameException

Dentro de screenplay hay un método llamado orComplainWith() que se puede usar para
manejar excepciones de una manera personalizada y que respete el principio de que las
pruebas deben ser documentación y su uso es como el siguiente:

Para conocer más sobre el manejo de excepciones por favor dirigirse a la siguiente
documentación: Domain-Specific Exceptions with Serenity BDD.
Nota: El nombramiento de objetos, clases, métodos, etc debe obedecer a la
búsqueda del mejoramiento de la legibilidad de los suites de pruebas y por tanto
del reporte. Un recomendación especial es realizar los desarrollos en inglés, ya
que screenplay está desarrollado en inglés y los métodos y clases que de el se
usen aparecerán en inglés en el reporte, esta recomendación se hace con el fin de
estandarizar el lenguaje.

Implementación en automatización web:

1. Verificar que se tenga el plugin de gradle y cucumber.

2. Crear proyecto gradle. (Aunque puede ser maven).

3. Borrar los archivos generados por default en caso de ser necesario. Esto depende mucho
del IDE con el que se esté trabajando.

4. Agregar el stack de tecnologías que se va a utilizar, para este caso se necesita, Junit,
Serenity, Cucumber, ScreenPlay y Hamcrest. Para esto debemos agregar las siguientes
líneas al archivo build.gradle en la raíz del proyecto. (TODO: Buscar como se haría para
maven).

https://github.com/antonioBlandon/GoogleSuiteScreenPlayGradle/blob/master/build.grad
le

5. Se crea el arquetipo del proyecto. Se trabaja bajo el modelo ScreenPlay. La arquitectura


con la que se trabaja se observa en la figura 7.

I. Se crea el package en las carpetas main y test. En algunos IDE’s será necesario
crear dichas carpetas. La guía para crear el package es el siguiente:

Url inversa de la compañía seguido por el nombre del área o departamento y


posteriormente el nombre del proyecto, y siguiendo como regla que no debe
contener mayúsculas.

Ej:

co.com.bancolombia.certification.googletranslate

url inversa: co.com.bancolombia


area: certification
nombre del proyecto: googletranslate

II. Se debe crear un nuevo source folder con la ruta: src/test/resources, y dentro de
este la capa features representada como paquete.

III. Las capas Tasks, Interactions, Questions, User Interface y las transversales (Utils,
Model y Exception) van en la carpeta principal, main. Por lo tanto se deben crear
dichos paquetes en esta ruta.
IV. Las capas Runners y Step Definitions van en la carpeta test. Por lo tanto se deben
crear dichos paquetes en esta ruta.

6. Se crear el archivo youtube.feature, donde se describen las características y los


escenarios a probar haciendo uso de la notación GHERKIN.

7. Se crea la clase YoutubeRunner en el paquete runners ubicado en la ruta src/test/java.

8. Se agregan las etiquetas necesarias para ejecutar los tests en la clase YoutubeRunner
(dichas etiquetas se deben agregar antes de la definición de la clase), que son las
siguientes:

@RunWith(CucumberWithSerenity.class)
@CucumberOptions(
features = "src/test/resources/features/google_translate.feature",
glue = "co.com.bancolombia.certification.googlesuite.stepdefinitions",
snippets = SnippetType.CAMELCASE
)

La anotación @RunWith me indica el framework con el que se va correr el test, en este


caso se indica que se va correr con CucumberWithSerenity pero se pudo haber indicado
por ejemplo que corra con Junit, lo cual no aplica para el caso.

@CucumberOptions define los parámetros necesarios para el test. En features se pone


la ruta donde se encuentra ubicado nuestro archivo .feature con las características que
deseamos implementar y cada uno de los escenarios que se puedan tener. En glue se
ubica la ruta donde se va generar la clase con el esqueleto de los test con los distintos
casos que se hayan definido. Por ultimo esta el parámetro snippets en el cual se indica
que la clase que se va generar respeta el modelo camelCase para el nombramiento de
clases y métodos.

OJO! Notese que el glue tiene notación de paquete a diferencia de features.

9. Se guardan los cambios realizados en YoutubeRunner y se ejecuta como Junit Test.

10. Se revisa la consola para ver los resultados de correr YoutubeRunner como Junit Test.
De la consola podemos obtener nuestro esqueleto que se debe agregar a la clase
YoutubeStepDefinitions, la cual, se debe crear en el paquete stepdefinitions.

11. Se descarga el driver del siguiente enlace:

https://chromedriver.storage.googleapis.com/index.html?path=2.40/

En dicho enlace hay varias opciones para descargar y dependerá de la máquina donde
se vayan a realizar las pruebas la opción que se deba seleccionar. El driver que se va a
utilizar es el de navegador de google chrome, pero el framework de Serenity soporta otros
navegadores como Firefox e internet explorer. (TODO: Buscar como se hace con los otros
navegadores).
12. Agregar e instanciar el driver descargado. El driver se debe agregar en la raíz del proyecto
y su instancia se realiza en la clase YoutubeStepDefinitions y se hace de la siguiente
manera:

@Managed(driver = "chrome")
private WebDriver herBrowser;

13. Se crea el actor en la clase YoutubeStepDefinition.

14. Se crea la tarea para abrir el navegador, la cual, consta de los siguientes pasos:

i. Se crea una clase que represente la acción que se desea realizar, en este caso,
abrir el navegador, por lo tanto la clase se nombra como OpenTheBrowser y se
debe implementar a partir de la interfaz Task para que se reconozca como una
tarea.

ii. Se realiza una configuración previa del ambiente, indicando que el actor puede
abrir un navegador desde la clase YoutubeStepDefinitions como se observa a
continuación.

@Managed(driver = "chrome")
private WebDriver herBrowser;

private Actor antonio = Actor.named("Antonio");

@Before
public void setUp() {
antonio.can(BrowseTheWeb.with(herBrowser));
}

iii. Se crea la clase que indica la página que va abrir el navegador, en este caso la
página inicial de youtube, por lo tanto se pone un nombre representativo a dicha
clase como YoutubeHomePage. Dicha clase, para respetar la arquitectura del
proyecto, debe alojarse en el paquete userinterfaces.

iv. Se ejecuta la tarea en el given del YouTubeStepDefinitions.

15. Después de abrir la página, se realiza la tarea de interés (buscar una canción). Para
desarrollar la tarea se deben seguir los siguientes pasos:

i. En el when del YoutubeStepDefinitoins se realiza dicho proceso manteniendo la


idea de que el nombre de las clases y métodos expresan la acción que se lleva a
cabo, como se observa a continuación:

@When("^he search the song crazy$")


public void heSearchTheSongCrazy() {
antonio.wasAbleTo(Search.the(song));
}
ii. Como se observa en el paso anterior se debe crear la clase Search y en esta el
método the(). La clase Search por lo tanto debe implementar la interfaz Task de
Serenity y debe agregarse en el paquete de tasks.

iii. En la clase Search debe implementarse los métodos necesarios que solicita la
interfaz Task.

16. Para realizar la tarea se necesita la referencia del botón de buscar y del text area donde
se digita el nombre del video que se busca, para realizar dicho proceso se debe realizar
una inspección de la página y ubicar los id de cada elemento en la medida de lo posible,
en caso contrario, se debe hacer uso de xPath como se hizo para el text area y que se
observa a continuación:

public static Target TARGET_SOURCE_TEXT_AREA = Target.the("Source Text


Area").locatedBy(String.format("//input[@id='search']"));
public static Target TARGET_BUTTON_SEARCH = Target.the("Button
Search").located(By.id("search-icon-legacy"));

Como se observó el tipo de datos en el que se definen los elementos son los Target que
son de la librería de Screenplay de Serenity.

17.
Cibergrafía
i. Introducción y patrón builder

• http://migranitodejava.blogspot.com/search/label/Introduccion%20a%20Patrones

• http://www.vogella.com/tutorials/DesignPatternBuilder/article.html

ii. Introducción Gherkin

• https://www.genbeta.com/desarrollo/bdd-cucumber-y-gherkin-desarrollo-dirigido-
por-comportamiento

• https://www.adictosaltrabajo.com/tutoriales/bdd-con-cucumber/

iii. Xpath

• https://www.guru99.com/xpath-selenium.html

iv. Screen Play

• Serenity and the Screenplay pattern

• Beyond Page Object: Liberate yourself from the chains of UI-Think!

• User-centric and task-driven: A better way to automate


• User-Centred Design: How a 50 year old technique became the key to scalable
test automation

• Beyond Page Objects: Next Generation Test Automation with Serenity and the
Screenplay Pattern

Y los siguientes videos:

• Serenity BDD Screenplay Demonstration - Part 1

• Serenity BDD Screenplay Demonstration - Part 2

• TestingAR XIX - Investing in Testing - Luz, cámara, Screenplay! por Rodrigo


Martin

También podría gustarte