1. Code
  2. Coding Fundamentals
  3. Testing

Pruebas funcionales sin interfaz gráfica con Selenium y PhantomJS

Scroll to top

Spanish (Español) translation by Luis Chiabrera (you can also view the original English article)

Construyamos un sistema para realizar pruebas funcionales en aplicaciones web, usando Selenium y PhantomJS. El sistema resultante nos permitirá escribir escenarios de prueba sencillos en JavaScript y probar esos escenarios tanto en navegadores reales como en un simulador sin interfaz gráfica.


Eligiendo componentes

La desventaja obvia de Selenium es que requiere un escritorio gráfico completo para todas y cada una de las pruebas.

Para empezar, tenemos que elegir un control de navegador o un motor de emulación para simular un usuario final. Durante mucho tiempo, el jugador principal en este campo fue Selenium, y todavía lo es. Selenium permite el control automatizado de navegadores reales en sistemas operativos reales, que es su principal ventaja: puedes estar absolutamente seguro de que las pruebas representan la realidad lo más fielmente posible.

La desventaja obvia de Selenium es que requiere un escritorio gráfico completo para todas y cada una de las pruebas. Como resultado, tus pruebas pueden volverse lentas. Sin embargo, Selenium puede ser fantástico, si tienes los recursos necesarios para configurar máquinas virtuales para diferentes sistemas operativos y conectarlos todos juntos.

En el extremo opuesto del espectro está PhantomJS: un proyecto pequeño, pero excelente, que ejecuta un motor WebKit con acceso completo a JavaScript, pero sin la parte gráfica. PhantomJS es muy fácil de configurar, se ejecuta en cualquier máquina y es significativamente más rápido.

Selenium ahora puede controlar PhantomJS de la misma manera que lo hace con cualquier otro navegador.

PhantomJS, al ser un WebKit completo, cubre el 90% de tus necesidades de pruebas funcionales. Después de todo, si tu aplicación se ejecuta correctamente en WebKit, es probable que se ejecute correctamente en otros navegadores. Obviamente, esto excluye Internet Explorer 6–8.

Sin embargo, a medida que tu proyecto se vuelve cada vez más popular, ese 10% restante se convierte en un problema importante. Si tu set de pruebas funcionales está configurado en PhantomJS directamente, sería complicado reescribir las pruebas para Selenium.

Afortunadamente, recientemente, cerca del final de 2012, recibimos un regalo en forma de enlaces PhantomJS a Selenium. En otras palabras, Selenium ahora puede controlar PhantomJS de la misma manera que lo hace con cualquier otro navegador.

Dado que Selenium, en sí mismo, no necesita ninguna configuración complicada y puede ejecutarse en cualquier lugar, podemos usar enlaces de Selenium para controlar PhantomJS y cubrir el 90% de nuestras necesidades de prueba. Si luego necesitas pruebas más potentes, puedes configurar conexiones de navegador adicionales a Selenium sin cambiar una sola línea en tu código.

Por lo tanto, nuestra elección de motor de navegador es Selenium con PhantomJS.

Describiendo las pruebas

Selenium ofrece enlaces en los lenguajes de programación más populares, por lo que podemos elegir un lenguaje de acuerdo a nuestras necesidades. Esta es quizás la parte más controvertida de este artículo: considero que JavaScript es la mejor opción para describir pruebas funcionales para sitios web y aplicaciones web.

  • Independientemente de la tecnología de back-end que utilices, tu front-end siempre utilizará JavaScript (esto se aplica incluso si utilizas un lenguaje que se compila en JavaScript vanilla, como CoffeeScript o TypeScript). Como tal, JavaScript siempre será un lenguaje entendido por al menos una persona de tu equipo.
  • A continuación, considera la posibilidad de que tus pruebas funcionales sean escritas por no programadores. La popularidad de JavaScript en el front-end, combinada con la expresividad en la capacidad de crear lenguajes claros específicos de dominio, claramente permite que más personas escriban pruebas funcionales.
  • Por último, es natural controlar un navegador de prueba con JavaScript, dado que es altamente asincrónico y es con lo que controlamos el navegador a diario.

Los enlaces de Selenium para JavaScript se denominan webdriverjs. Aunque el proyecto es menos maduro que los controladores admitidos oficialmente para Java, C #, Ruby y Python, ya contiene la mayor parte de la funcionalidad que necesitamos.

Haciendo pruebas

Para los propósitos de este artículo, se ha seleccionado Mocha con Chai.

Finalmente, necesitamos un corredor de pruebas, o una aplicación para ejecutar las pruebas por nombre, e imprimir la salida de forma bonita, mientras anotamos cuántas pruebas tuvieron éxito o fallaron. Este ejecutor de pruebas también debe ofrecer una biblioteca de aserciones, que permite al codificador expresar si una prueba tiene éxito o falla.

La elección es absolutamente libre aquí. Hay muchos corredores de pruebas de JavaScript, pero para los propósitos de este artículo, se ha seleccionado Mocha con Chai. Mocha proporciona una cantidad considerable de flexibilidad, una amplia variedad de formatos de salida y la popular sintaxis similar a Jasmine. Chai le permite escribir afirmaciones descriptivas similares a BDD.


Configuración

Aquí está la pila final que usaremos:

  1. Mocha - corredor de pruebas
  2. Chai - biblioteca de afirmaciones
  3. webdriverjs - enlaces de control del navegador
  4. Selenium - abstracción del navegador y fábrica en ejecución
  5. PhantomJS - navegador rápido sin interfaz grafica

Node.js y npm

Debido a que la mayor parte de nuestra pila se basa en JavaScript, necesitamos node.js y npm. Ambas son herramientas comunes en la comunidad y asumiré que ya las tiene configuradas. De lo contrario, usa el instalador en el sitio web de node.js. No te preocupes; Si algo sale mal, hay muchas guías de instalación de Node disponibles en la web.

Mocha, Chai y  webdriverjs

Los tres se pueden instalar usando npm:

1
sudo npm install -g mocha chai webdriverjs

Alternativamente, puedes instalarlos localmente en el directorio donde se encuentran tus pruebas:

1
npm install mocha chai webdriverjs

Selenium

Descarga Selenium Server. Se distribuye como un solo archivo jar, que ejecutas simplemente:

1
java -jar selenium-server-standalone-2.28.0.jar

Tan pronto como ejecutes este comando, arrancará un servidor al que se conectará tu código de prueba más adelante. Ten en cuenta que deberás ejecutar Selenium Server cada vez que ejecutes tus pruebas.

PhantomJS

Versión rapida 

Utiliza npm para instalar PhantomJS globalmente:

1
sudo npm install -g phantomjs

Otras opciones

Requerimos una versión nueva de PhantomJS, al menos 1.8. Esto significa que los paquetes proporcionados por tu administrador de paquetes (apt-get, MacPorts, ...) probablemente estarán desactualizados.

Puedes instalar usando npm sin una instalación global o usando otros métodos manualmente. En este caso, sin embargo, tendrás que decirle a Selenium dónde colocaste PhantomJS cada vez que ejecute Selenium:

1
PATH="/path/to/node_modules/phantomjs/bin:$PATH" java -jar selenium-server-standalone-2.28.0.jar

Combinando todo

Ahora que tenemos todas las piezas, tenemos que juntar todo.

Recuerda: antes de ejecutar cualquier prueba, debes ejecutar Selenium Server:

1
java -jar selenium-server-standalone-2.28.0.jar

Selenium ejecutará PhantomJS internamente; no tienes que preocuparte por eso.

Ahora, necesitamos conectarnos a Selenium desde nuestro JavaScript. Aquí hay un fragmento de muestra, que iniciará una conexión con Selenium y tendrá un objeto listo para controlar nuestra instancia de Selenium:

1
// Use webdriverjs to create a Selenium Client
2
var client = require('webdriverjs').remote({
3
    desiredCapabilities: {
4
        // You may choose other browsers
5
        // http://code.google.com/p/selenium/wiki/DesiredCapabilities
6
        browserName: 'phantomjs'
7
    },
8
    // webdriverjs has a lot of output which is generally useless
9
    // However, if anything goes wrong, remove this to see more details
10
    logLevel: 'silent'
11
});
12
13
client.init();

Ahora, podemos describir nuestras pruebas y usar la variable client para controlar el navegador. Una referencia completa de la API webdriverjs está disponible en la documentación, pero aquí hay un breve ejemplo:

1
client.url('http://example.com/')
2
client.getTitle(function(title){
3
    console.log('Title is', title);
4
});
5
client.setValue('#field', 'value');
6
client.submitForm();
7
client.end();

Usemos la sintaxis de Mocha y Chai para describir una prueba; probaremos algunas propiedades de la página web example.com:

1
describe('Test example.com', function(){
2
    before(function(done) {
3
        client.init().url('http://example.com', done);
4
    });
5
6
    describe('Check homepage', function(){
7
        it('should see the correct title', function(done) {
8
            client.getTitle(function(title){
9
                expect(title).to.have.string('Example Domain');
10
                done();
11
            });
12
        });
13
14
        it('should see the body', function(done) {
15
            client.getText('p', function(p){
16
                expect(title).to.have.string(
17
                    'for illustrative examples in documents.'
18
                );
19
                done();
20
            })
21
        });
22
    });
23
24
    after(function(done) {
25
        client.end();
26
        done();
27
    });
28
});

Es posible que desees compartir la inicialización client en varios archivos de prueba. Crea un pequeño módulo de nodo para inicializarlo e importarlo en cada archivo de prueba:

client.js:

1
exports.client = require('webdriverjs').remote({
2
    // Settings
3
};

test.js:

1
var client = require('./client').client;
2
var expect = require('chai').expect;
3
4
// Perform tests

Ejecutando

Las suites de prueba de Mocha se ejecutan con el binario mocha. Si seguiste esta guía e instalaste Mocha localmente, entonces debes describir una ruta completa al binario tu mismo: node_modules/mocha/bin/mocha.

De forma predeterminada, Mocha trata cualquier prueba que demore más de dos segundos como fallida. Dado que en realidad estamos inicializando un navegador web y realizando una solicitud HTTP, necesitamos aumentar este tiempo de espera a 5 o 10 segundos:

1
node_modules/mocha/bin/mocha test.js -t 10000

Si todo salió de acuerdo con el plan, deberías ver un resultado como este:

1
  .
2
3
  ✔ 1 <span class="nb">test complete</span>

Los siguientes pasos

Una vez que hayas logrado los resultados de las pruebas funcionales deseados, es posible que desees considerar mejorar aún más tu configuración.

Dos direcciones obvias son la integración continua y las pruebas de Selenium distribuidas.

Integración continua

Tu objetivo debe ser minimizar el tiempo que dedicas a realizar pruebas.

Es posible que desees utilizar un servidor de integración continua completamente automático, que ejecutará las pruebas automáticamente cuando sea necesario y te informará si algo sale mal.

En el mundo del código abierto, la función de dicho servidor está cubierta por Jenkins CI: un servicio conveniente, potente y fácil de instalar, que ejecutará las pruebas siempre que sea necesario, las ejecutará en cualquier configuración que tú proporciones y posiblemente ejecutará muchas más tareas relacionadas con la compilación, como implementar tu código en servidores remotos.

Alternativamente, si te sientes aventurero, puedes experimentar con un nuevo proyecto, llamado GitLab CI, que ofrece menos funciones, pero se ve mejor y está integrado con GitLab, un clon de GitHub autohospedado.

En cualquier caso, tu objetivo debe ser minimizar el tiempo que dedicas a realizar pruebas. En cambio, las pruebas deben ejecutarse automáticamente y solo deben informarte si algo sale mal.

Cuadrícula de Selenium

Selenium tiene una serie de limitaciones de implementación. Por ejemplo, no puedes ejecutar más de unos pocos navegadores en la misma máquina para probarlos con Selenium.

Además, notarás que, una vez que tengas muchas pruebas, ejecutarlas todas puede convertirse en un proceso largo. Aunque la integración continua alivia en parte este problema, es posible que desees ejecutar algunas pruebas en paralelo en diferentes máquinas.

Finalmente, pronto notarás que deseas probar diferentes navegadores en diferentes sistemas operativos. Y, aunque tu código de prueba puede, en teoría, comunicarse con diferentes servidores de Selenium, una vez que crece un poco, esta configuración necesita centralización.

La configuración de Selenium Grid intenta proporcionar exactamente eso. En lugar de tener un servidor Selenium que controle varios navegadores en una máquina, tiene un servidor Selenium, que controla múltiples nodos Selenium, cada uno de los cuales controla solo unos pocos navegadores en un único sistema operativo.


Conclusión

La pila resultante, aunque no trivial, en realidad, es bastante simple. La adición de PhantomJS al final de Selenium nos permite comenzar a usar Selenium sin mucha inversión inicial, como configurar servidores de prueba gráfica.

El uso de JavaScript como motor de prueba asegura que nuestras pruebas se mantendrán relevantes en el contexto del desarrollo web en el futuro previsible.