Hola Habr!
Mi nombre es Vitaliy Kotov, realizo muchas pruebas de automatización y me gusta. Recientemente participé en un proyecto para configurar la automatización desde cero en la pila TypeScript + Protractor + Jasmine. Para mí, esta pila era nueva y busqué la información necesaria en Internet.
Logré encontrar los manuales más útiles y razonables solo en inglés. Decidí que en ruso también necesitaba hacer esto. Solo le diré lo básico: por qué una pila de este tipo, qué necesita configurar y cómo se ve la prueba más simple.
Debo decir de inmediato que rara vez trabajo con NodeJS, npm y con JavaScript del lado del servidor en general (especialmente con TypeScript). Si encuentra un error en la terminología en alguna parte o algunas de mis decisiones pueden mejorarse, me alegrará saberlo en los comentarios de personas más experimentadas.
Por cierto, ya tenía un artículo similar:
"Implementamos la automatización en un par de horas: PHPUnit, Selenium, Composer" .

Desafío
En primer lugar, descubramos qué problema estamos resolviendo. Tenemos una aplicación web escrita usando AngularJS. Este es un marco de trabajo basado en JavaScript en el que a menudo se escriben proyectos web.
En este artículo, no consideraremos los pros y los contras de los proyectos de AngularJS. Solo unas pocas palabras sobre las características de tales proyectos en términos de escribir pruebas e2e para ellos.
Un aspecto bastante importante de la automatización de pruebas es trabajar con elementos de página, lo que sucede con la ayuda de localizadores. Un localizador es una línea compuesta de acuerdo con ciertas reglas e identifica un elemento de la interfaz de usuario: uno o más.
Para la web, CSS y Xpath son los más utilizados. A veces, si hay un elemento con un ID único en la página, puede buscarlo. Sin embargo, me parece que WebDriver todavía convierte este ID en un localizador CSS al final y ya está trabajando con él.
Si miramos el código HTML de algún proyecto de AngularJS, veremos muchos atributos para elementos que no están en HTML clásico:

El código se toma de la página de
demostración del
transportador .
AngularJS utiliza todos los atributos que comienzan con ng- * para trabajar con la interfaz de usuario. Una situación bastante típica es cuando los elementos distintos de estos atributos de control no tienen otros, lo que complica el proceso de compilación de localizadores de calidad.
Aquellos que han realizado una gran cantidad de automatización conocen el valor de tales IU para las cuales se pueden construir fácilmente los localizadores. Después de todo, esto es raro para grandes proyectos. :)
En realidad, para tal proyecto, también necesitamos configurar la automatización de prueba. Vamos!
Que es que
En primer lugar, descubramos por qué se necesita cada componente de nuestra pila.
Transportador es un marco de prueba basado en WebDriverJS. Será él quien lanzará nuestros navegadores, hará que abran las páginas necesarias e interactúen con los elementos necesarios.
Este marco está diseñado específicamente para proyectos de AngularJS. Proporciona formas adicionales de especificar localizadores:
element(by.model('first')); element(by.binding('latest')); element(by.repeater('some'));
Puede encontrar una lista completa en la página del
manual .
Estos métodos simplifican la creación y el soporte de localizadores en un proyecto. Sin embargo, debe comprender que "bajo el capó" todo esto, en cualquier caso, se convierte a CSS. El hecho es que el protocolo W3C, en base al cual tiene lugar la interacción en WebDriver, solo puede funcionar con un conjunto finito de localizadores. Esta lista se puede ver en
w3.org .
TypeScript es un lenguaje de programación creado por Microsoft. TypeScript difiere de JavaScript en la capacidad de escribir variables, el soporte para el uso de clases completas y la capacidad de conectar módulos.
Escrito en código TS para trabajar con el motor V8 se traduce en código JS, que ya se está ejecutando. Durante esta transformación, se verifica el cumplimiento del código. Por ejemplo, no se "compila" si, en lugar de int, una cadena se pasa explícitamente a la función en alguna parte.
Jasmine es un marco para probar el código JavaScript. De hecho, es gracias a él que nuestro código JS se convierte en lo que solíamos llamar una prueba. Él maneja estas pruebas.
A continuación nos fijamos en sus capacidades.
Montaje y configuración del proyecto
Bueno, nos decidimos por un conjunto de marcos, ahora vamos a armar todo esto.
Para trabajar con el código, elegí
Visual Studio Code de Microsoft. Aunque muchos escriben en
WebStorm o incluso en
Intellij Idea de JetBrains.
Ya instalé NodeJS (v11.6.0) y NPM (6.9.0). Si no lo tiene, esto no es un problema; instalarlos no es difícil. Todo se describe con suficiente detalle en el
sitio web oficial .
Se puede usar hilo en lugar de NPM, aunque esto no es importante para un proyecto pequeño.
En nuestro IDE estamos creando un nuevo proyecto. Creamos package.json en la raíz del proyecto; en él describiremos todos los paquetes que necesitamos para el proyecto.
Puede crearlo usando el comando
npm init . O simplemente puede copiar el contenido a un archivo.
Inicialmente,
package.json se ve así:
{ "name": "protractor", "dependencies": { "@types/node": "^10.5.2", "@types/jasmine": "^3.3.12", "protractor": "^5.4.2", "typescript": "^3.4.1" } }
Después de eso, ejecutamos el comando
npm install para instalar todos los módulos necesarios y sus dependencias (bueno, recuerdas la imagen de algo que es más pesado que un agujero negro ...)
Como resultado, deberíamos tener un directorio node_modules. Si ella apareció, entonces todo va de acuerdo al plan. Si no, vale la pena analizar el resultado de la ejecución del comando, por lo general, todo se describe con bastante detalle allí.
TypeScript y su configuración
Para instalar TypeScript, necesitamos npm:
npm install -g typescript
Asegúrese de que esté instalado:
$ tsc -v Version 3.4.1
Todo parece estar en orden.
Ahora necesitamos crear una configuración para trabajar con TS. También debe estar en la raíz del proyecto y llamarse
tsconfig.jsonSu contenido será así:
{ "compilerOptions": { "lib": ["es6"], "strict": true, "outDir" : "output_js", "types" : ["jasmine", "node"] }, "exclude": [ "node_modules/*" ] }
En resumen, especificamos lo siguiente en esta configuración:
- En qué directorio colocar el código JS final (en nuestro caso es output_js)
- Habilitar modo estricto
- Indicado con qué marcos estamos trabajando
- Excluido node_modules de la compilación
TS tiene una gran variedad de configuraciones. Estos son suficientes para nuestro proyecto. Puede obtener más información
en typescriptlang.org .
Ahora veamos cómo funciona el comando
tsc , que convertirá nuestro código TS en código JS. Para hacer esto, cree un archivo simple check_tsc.ts con el siguiente contenido:
saySomething("Hello, world!"); function saySomething(message: string) { console.log(message); }
Y luego ejecute el comando
tsc (para esto necesita estar dentro del directorio del proyecto).
Veremos que tenemos el directorio output_js y ha aparecido un archivo js similar con los siguientes contenidos:
"use strict"; saySomething("Hello, world!"); function saySomething(message) { console.log(message); }
Este archivo ya se puede iniciar con el comando de nodo:
$ node output_js/check_tsc.js Hello, world!
Entonces, escribimos nuestro primer programa TypeScipt, felicidades. Escribamos pruebas ahora. :)
Configuración del transportador
Para el transportador también necesitamos una configuración. Pero ya no será en forma de json, sino en forma de archivo ts. Llamémoslo config.ts y escriba el siguiente código allí:
import { Config } from "protractor"; export const config: Config = { seleniumAddress: "http://127.0.0.1:4444/wd/hub", SELENIUM_PROMISE_MANAGER: false, capabilities: { browserName: "chrome", }, specs: [ "Tests/*Test.js", ] };
En este archivo especificamos lo siguiente:
En primer lugar, la ruta al servidor Selenium en ejecución. Es bastante simple de ejecutar, solo necesita
descargar el archivo jar del Servidor independiente y los controladores necesarios (por ejemplo, el controlador
de Chrome para el navegador Chrome ). Luego, escriba el siguiente comando:
java -jar -Dwebdriver.chrome.driver=/path/to/chromedriver /path/to/selenium-server-standalone.jar
Como resultado, deberíamos ver la siguiente conclusión:
23:52:41.691 INFO [GridLauncherV3.launch] - Selenium build info: version: '3.11.0', revision: 'e59cfb3' 23:52:41.693 INFO [GridLauncherV3$1.launch] - Launching a standalone Selenium Server on port 4444 2019-05-02 23:52:41.860:INFO::main: Logging initialized @555ms to org.seleniumhq.jetty9.util.log.StdErrLog 23:52:42.149 INFO [SeleniumServer.boot] - Welcome to Selenium for Workgroups.... 23:52:42.149 INFO [SeleniumServer.boot] - Selenium Server is up and running on port 4444
El puerto 4444 está predeterminado. Se puede configurar utilizando el parámetro -port o mediante el parámetro de configuración "seleniumArgs" => "-port".
Si quiere más fácil y más rápido: puede descargar el paquete
npm webdriver-manager .
Y luego administre el servidor utilizando los comandos de inicio, apagado, etc. No hay mucha diferencia, es solo que estoy más acostumbrado a trabajar con un archivo jar. :)
En segundo lugar , indicamos que no queremos usar el administrador de Promise. Más sobre esto más tarde.
En tercer lugar , especificamos capacidades para nuestro navegador. Comenté una parte, por ejemplo, que podemos iniciar fácilmente el navegador en modo sin cabeza. Esta es una característica interesante, pero no le permitirá observar visualmente nuestras pruebas. Mientras tanto, solo estamos aprendiendo, me gustaría. :)
Cuarto , especificamos una máscara para las especificaciones (pruebas). Todo lo que se encuentra en la carpeta Pruebas y termina con Test.js. ¿Por qué en js, no ts? Esto se debe a que, al final, Node funcionará específicamente con archivos JS y no con archivos TS. Es importante no confundirse, especialmente al comienzo del trabajo.
Ahora cree la carpeta Pruebas y escriba la primera prueba. Él hará lo siguiente:
- Inhabilita la comprobación de que esta es una página angular. Sin esto, obtenemos este mensaje de error: Error al ejecutar testForAngular. Por supuesto, para las páginas angulares no es necesario desactivar esta verificación.
- Va a la página de Google.
- Verifique que haya un campo de entrada de texto.
- Ingrese el texto "transportador".
- Haga clic en el botón Enviar (tiene un localizador bastante complicado, ya que hay dos botones y el primero es invisible).
- Se espera que la URL contenga la palabra "transportador"; esto significa que hicimos todo bien y comenzó la búsqueda.
Aquí está el código que obtuve:
import { browser, by, element, protractor } from "protractor"; describe('Search', () => { it('Open google and find a text', async () => {
En el código, vemos que todo comienza con la función describe (). Ella vino a nosotros desde el marco de Jasmine. Este es un contenedor para nuestro script. En su interior, puede haber funciones beforeAll () y beforeEach () para realizar cualquier manipulación antes de todas las pruebas y antes de cada prueba. Tantas funciones como () son, de hecho, nuestras pruebas. Al final, si está definido, afterAll () y afterEach () se ejecutarán para manipulaciones después de cada prueba y todas las pruebas.
No hablaré sobre todas las características de Jasmine, puedes leer sobre ellas en el sitio web
jasmine.imtqy.comPara ejecutar nuestra prueba, primero debe convertir el código TS en código JS y luego ejecutarlo:
$ tsc $ protractor output_js/config.js
Nuestra prueba comenzó, somos geniales. :)

Si la prueba no comenzó, vale la pena verificar:
- Que el código está escrito correctamente. En general, si hay errores críticos en el código, los detectaremos durante el comando tsc.
- Ese servidor Selenium se está ejecutando. Para hacer esto, puede abrir la URL http: //127.0.0.1-00-00444/wd/hub ; debe haber una interfaz para las sesiones de Selenium.
- Ese Chrome comienza normalmente con la versión descargada de chrome-driver. Para hacer esto, en la página wd / hub /, haga clic en Crear sesión y seleccione Chrome. Si no se inicia, debe actualizar Chrome o descargar otra versión de chrome-driver.
- Si todo esto falla, puede verificar que el comando de instalación npm se haya completado con éxito.
- Si todo está escrito correctamente, pero aún no comienza nada, intente googlear el error. A menudo ayuda. :)
NPM scripts
Para hacer la vida más fácil, puede hacer parte de los comandos alias npm. Por ejemplo, me gustaría eliminar el directorio con archivos JS anteriores y volver a crearlo con otros nuevos antes de cada ejecución de prueba.
Para hacer esto, agregue el elemento de scripts a package.json:
{ "name": "protractor", "scripts": { "test": "rm -rf output_js/; tsc; protractor output_js/config.js" }, "dependencies": { "@types/node": "^10.5.2", "@types/jasmine": "^3.3.12", "protractor": "^5.4.2", "typescript": "^3.4.1" } }
Ahora ingresando el
comando de prueba npm sucederá lo siguiente: el directorio output_js con el código anterior se eliminará, se creará de nuevo y se escribirá un nuevo código JS. Después de lo cual las pruebas comenzarán de inmediato.
En lugar de este conjunto de comandos, puede especificar cualquier otro que necesite trabajar personalmente. Por ejemplo, puede iniciar y apagar un servidor de selenio entre las ejecuciones de prueba. Aunque esto, por supuesto, es más fácil de controlar dentro del propio código de prueba.
Un poco sobre la promesa
Al final, hablaré un poco sobre Promise, async / wait y cómo la escritura de pruebas en NodeJS difiere de la misma Java o Python.
JavaScript es un lenguaje asincrónico. Esto significa que el código no siempre se ejecuta en el orden en que está escrito. Esto incluye solicitudes HTTP, y recordamos que el código se comunica con Selenium Server a través de HTTP.
Promise (generalmente llamado "promesas") proporciona una forma conveniente de organizar el código asincrónico. Puede leer más sobre ellos en
learn.javascript.ru .
De hecho, estos son objetos que hacen que un código dependa de la ejecución de otro, lo que garantiza un cierto orden. Transportador trabaja muy activamente con estos objetos.
Veamos un ejemplo. Supongamos que ejecutamos el siguiente código:
driver.findElement().getText();
En Java, esperamos que regresemos un objeto de tipo String. En Protractor, esto no es así, devolveremos un objeto Promise. Y así, imprimirlo con un objetivo de depuración no funcionará.
Por lo general, no necesitamos imprimir el valor resultante. Necesitamos pasarlo a algún otro método que ya funcione con este valor. Por ejemplo, verificará que el texto cumpla con lo esperado.
Métodos similares en Protractor también aceptan objetos Promise como entrada, por lo que no hay problema. Pero, si aún desea ver el valor, entonces () será útil.
Así es como podemos imprimir el texto del botón en una página de Google (tenga en cuenta que, dado que se trata de un botón, el texto está dentro del atributo de valor):
En cuanto a las palabras clave async / await, este es un enfoque un poco más nuevo para trabajar con código asincrónico. Le permite evitar la promesa del infierno, que se formó previamente en el código debido a la gran cantidad de anidamiento. Sin embargo, no podrá deshacerse completamente de Promise y deberá poder trabajar con ellos. Esto es comprensible y detallado se puede encontrar en el artículo
Diseño asíncrono / espera en JavaScript: fortalezas, dificultades y características de uso .
Tarea
Como tarea, sugiero escribir pruebas para una página escrita en AngularJS:
protractor-demo .
No olvide eliminar la línea del código sobre desactivar la comprobación de página en AngularJS. Y asegúrese de trabajar con localizadores diseñados específicamente para AngularJS. No hay magia particular en esto, pero es bastante conveniente.
Resumen
Hagamos un balance. Logramos escribir pruebas que funcionan en un montón de TypeScript + Protractor + Jasmine. Aprendimos a construir un proyecto así, crear las configuraciones necesarias y escribimos la primera prueba.
En el camino, discutimos un poco sobre cómo trabajar con las pruebas automáticas de JavaScript. Parece bueno por un par de horas. :)
Qué leer, dónde mirar
El transportador tiene un manual bastante bueno con ejemplos de JavaScript:
https://www.protractortest.org/#/tutorialJasmine tiene un manual:
https://jasmine.imtqy.com/pages/docs_home.htmlTypeScipt tiene un buen comienzo:
https://www.typescriptlang.org/docs/handbook/typescript-in-5-minutes.htmlEn el medio, hay un buen artículo en inglés sobre TypeScript + Protractor + Pepino:
https://medium.com/@igniteram/e2e-testing-with-protractor-cucumber-using-typescript-564575814e4aY en mi repositorio publiqué el código final de lo que discutimos en este artículo:
https://github.com/KotovVitaliy/HarbProtractorJasmineJasmine .
En Internet puede encontrar ejemplos de proyectos más complejos y más grandes en esta pila.
Gracias por su atencion! :)