Pruebas automatizadas
La automatización de pruebas es una manera eficiente de validar que el código de la aplicación funciona como se pretende. Mientras Electron no mantiene activamente su propia solución de pruebas, esta guía recorrerá un par de maneras en que puedes ejecutar pruebas automatizadas de extremo a extremo en tu aplicación Electron.
Usando la interfaz WebDriver
Para ChromeDriver - WebDriver para Chrome:
WebDriver es una herramienta de código abierto para pruebas automatizadas de aplicaciones web en varios navegadores. Provee la capacidad de navegar por páginas web, sistema de usuarios, ejecución de JavaScript, y más. ChromeDriver es un servidor independiente que implementa el protocolo de cable de WebDriver para Chromium. Ha sido desarrollado por miembros de los equipos de Chromium y WebDriver.
Hay algunas maneras en que puedes configurar las pruebas usando WebDriver.
Con WebdriverIO
WebdriverIO (WDIO) es un framework para automatización de pruebas que provee un paquete Node.js para pruebas con WebDriver. Su ecosistema además incluye varios complementos (p. ej. reportes y servicios) que pueden ayudarte a armar su configuración de prueba.
Instalar el testrunner
Primero necesitas ejecutar el kit de herramientas de inicio WebdriverIO en el directorio raíz de tu proyecto:
- npm
- Yarn
npx wdio . --yes
npx wdio . --yes
Esto instala todos los paquetes necesarios para ti y genera un archivo de configuración wdio.conf.js
.
Conecta WDIO a tu aplicación Electron
Actualiza las capacidades en tu archivo de configuración para apuntar al binario de tu aplicación Electron:
export.config = {
// ...
capabilities: [{
browserName: 'chrome',
'goog:chromeOptions': {
binary: '/path/to/your/electron/binary', // Ruta a tu binario de Electron.
args: [/* cli arguments */] // Opcional, tal vez 'app=' + /ruta/hacia/tu/aplicación/
}
}]
// ...
}
Ejecutar tus pruebas
Para ejecutar tus pruebas:
$ npx wdio run wdio.conf.js
Con Selenium
Selenium es un framework de automatización web que expone enlaces a las APIs de WebDriver en muchos lenguajes. Sus enlaces Node.js están disponibles bajo el paquete selenium-webdriver
en NPM.
Ejecurar un servidor ChromeDriver
Para usar Selenium con Electron, necesitas descargar y ejecutar el binario electron-chromedriver
:
- npm
- Yarn
npm install --save-dev electron-chromedriver
./node_modules/.bin/chromedriver
Starting ChromeDriver (v2.10.291558) on port 9515
Only local connections are allowed.
yarn add --dev electron-chromedriver
./node_modules/.bin/chromedriver
Starting ChromeDriver (v2.10.291558) on port 9515
Only local connections are allowed.
Recuerde el puerto número 9515
, que usaremos más adelante.
Conectar Selenium a ChromeDriver
A continuación, instalar Selenium dentro de tu proyecto:
- npm
- Yarn
npm install --save-dev selenium-webdriver
yarn add --dev selenium-webdriver
El uso de Selenium-web driver
con Electron es el mismo que con sitios normales, excepto que tienes tiene que especificar manualmente como conectar el ChromeDriver y donde se encuentra el binario o ejecutable de Electron:
const webdriver = require('selenium-webdriver')
const driver = new webdriver.Builder()
// El "9515" es el puerto abierto por el ChromeDriver.
.usingServer('http://localhost:9515')
.withCapabilities({
'goog:chromeOptions': {
// Here is the path to your Electron binary.
binary: '/Path-to-Your-App.app/Contents/MacOS/Electron'
}
})
.forBrowser('chrome') // note: use .forBrowser('electron') for selenium-webdriver <= 3.6.0
.build()
driver.get('http://www.google.com')
driver.findElement(webdriver.By.name('q')).sendKeys('webdriver')
driver.findElement(webdriver.By.name('btnG')).click()
driver.wait(() => {
return driver.getTitle().then((title) => {
return title === 'webdriver - Google Search'
})
}, 1000)
driver.quit()
Usando Playwright
Microsoft Playwright es un framework de pruebas de extremo a extremo construido usando protocolos de depuración remotos específicos del navegador, similar a la API Node.js de Puppeteer "headless" pero dirigida hacia pruebas de extremo a extremo. Playwright tiene soporte experimental de Electron a través del soporte de Electron para el Protocolo de Chrome DevTools (CDP).
Instala las dependencias
Puede instalar Playwright a través de su gestor de paquetes Node.js preferido. El equipo de Playwright recomienda usar la variable de entorno PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD
para evitar descargas innecesarias del navegador al probar una aplicación Electron.
- npm
- Yarn
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1 npm install --save-dev playwright
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1 yarn add --dev playwright
Playwright también viene con su propio test runner, Playwright Test, que está construido para pruebas de punto a extremo. También puedes instalarlo como una dependencia de desarrollo en su proyecto:
- npm
- Yarn
npm install --save-dev @playwright/test
yarn add --dev @playwright/test
:::precaución Dependencias
Este tutorial fue escrito playwright@1.16.3
y @playwright/test@1.16.3
. Echa un vistazo a la página de versiones de Playwright para aprender sobre cambios que podrían afectar el código a continuación.
:::
Usando test runners de terceros
Si estás interesado en usar un test runner alternativo (por ejemplo, Jest o Mocha), echa un vistazo a la guía de Playwright Test Runners de Terceros .
Escribe tus tests
Playwright launches your app in development mode through the _electron.launch
API. To point this API to your Electron app, you can pass the path to your main process entry point (here, it is main.js
).
const { _electron: electron } = require('playwright')
const { test } = require('@playwright/test')
test('launch app', async () => {
const electronApp = await electron.launch({ args: ['main.js'] })
// close app
await electronApp.close()
})
After that, you will access to an instance of Playwright's ElectronApp
class. This is a powerful class that has access to main process modules for example:
const { _electron: electron } = require('playwright')
const { test } = require('@playwright/test')
test('get isPackaged', async () => {
const electronApp = await electron.launch({ args: ['main.js'] })
const isPackaged = await electronApp.evaluate(async ({ app }) => {
// This runs in Electron's main process, parameter here is always
// the result of the require('electron') in the main app script.
return app.isPackaged
})
console.log(isPackaged) // false (because we're in development mode)
// close app
await electronApp.close()
})
It can also create individual Page objects from Electron BrowserWindow instances. For example, to grab the first BrowserWindow and save a screenshot:
const { _electron: electron } = require('playwright')
const { test } = require('@playwright/test')
test('save screenshot', async () => {
const electronApp = await electron.launch({ args: ['main.js'] })
const window = await electronApp.firstWindow()
await window.screenshot({ path: 'intro.png' })
// close app
await electronApp.close()
})
Putting all this together using the PlayWright Test runner, let's create a example.spec.js
test file with a single test and assertion:
const { _electron: electron } = require('playwright')
const { test, expect } = require('@playwright/test')
test('example test', async () => {
const electronApp = await electron.launch({ args: ['.'] })
const isPackaged = await electronApp.evaluate(async ({ app }) => {
// This runs in Electron's main process, parameter here is always
// the result of the require('electron') in the main app script.
return app.isPackaged;
});
expect(isPackaged).toBe(false);
// Wait for the first BrowserWindow to open
// and return its Page object
const window = await electronApp.firstWindow()
await window.screenshot({ path: 'intro.png' })
// close app
await electronApp.close()
});
Then, run Playwright Test using npx playwright test
. You should see the test pass in your console, and have an intro.png
screenshot on your filesystem.
☁ $ npx playwright test
Running 1 test using 1 worker
✓ example.spec.js:4:1 › example test (1s)
info
Playwright Test will automatically run any files matching the .*(test|spec)\.(js|ts|mjs)
regex. You can customize this match in the Playwright Test configuration options.
Further reading
Check out Playwright's documentation for the full Electron and ElectronApplication class APIs.
Using a custom test driver
It's also possible to write your own custom driver using Node.js' built-in IPC-over-STDIO. Custom test drivers require you to write additional app code, but have lower overhead and let you expose custom methods to your test suite.
To create a custom driver, we'll use Node.js' child_process
API. El conjunto de pruebas generará el proceso de Electron, luego establecerá un protocolo de mensajería simple:
const childProcess = require('child_process')
const electronPath = require('electron')
// spawn the process
const env = { /* ... */ }
const stdio = ['inherit', 'inherit', 'inherit', 'ipc']
const appProcess = childProcess.spawn(electronPath, ['./app'], { stdio, env })
// listen for IPC messages from the app
appProcess.on('message', (msg) => {
// ...
})
// envía un mensaje IPC a la aplicación
appProcess.send({ my: 'message' })
From within the Electron app, you can listen for messages and send replies using the Node.js process
API:
// listen for messages from the test suite
process.on('message', (msg) => {
// ...
})
// send a message to the test suite
process.send({ my: 'message' })
Ahora podemos comunicar desde la suite de test a la aplicación Electron usando el objeto appProcess
.
For convenience, you may want to wrap appProcess
in a driver object that provides more high-level functions. Here is an example of how you can do this. Let's start by creating a TestDriver
class:
class TestDriver {
constructor ({ path, args, env }) {
this.rpcCalls = []
// start child process
env.APP_TEST_DRIVER = 1 // let the app know it should listen for messages
this.process = childProcess.spawn(path, args, { stdio: ['inherit', 'inherit', 'inherit', 'ipc'], env })
// handle rpc responses
this.process.on('message', (message) => {
// pop the handler
const rpcCall = this.rpcCalls[message.msgId]
if (!rpcCall) return
this.rpcCalls[message.msgId] = null
// reject/resolve
if (message.reject) rpcCall.reject(message.reject)
else rpcCall.resolve(message.resolve)
})
// wait for ready
this.isReady = this.rpc('isReady').catch((err) => {
console.error('Application failed to start', err)
this.stop()
process.exit(1)
})
}
// simple RPC call
// to use: driver.rpc('method', 1, 2, 3).then(...)
async rpc (cmd, ...args) {
// send rpc request
const msgId = this.rpcCalls.length
this.process.send({ msgId, cmd, args })
return new Promise((resolve, reject) => this.rpcCalls.push({ resolve, reject }))
}
stop () {
this.process.kill()
}
}
module.exports = { TestDriver };
In your app code, can then write a simple handler to receive RPC calls:
const METHODS = {
isReady () {
// do any setup needed
return true
}
// define your RPC-able methods here
}
const onMessage = async ({ msgId, cmd, args }) => {
let method = METHODS[cmd]
if (!method) method = () => new Error('Invalid method: ' + cmd)
try {
const resolve = await method(...args)
process.send({ msgId, resolve })
} catch (err) {
const reject = {
message: err.message,
stack: err.stack,
name: err.name
}
process.send({ msgId, reject })
}
}
if (process.env.APP_TEST_DRIVER) {
process.on('message', onMessage)
}
Then, in your test suite, you can use your TestDriver
class with the test automation framework of your choosing. The following example uses ava
, but other popular choices like Jest or Mocha would work as well:
const test = require('ava')
const electronPath = require('electron')
const { TestDriver } = require('./testDriver')
const app = new TestDriver({
path: electronPath,
args: ['./app'],
env: {
NODE_ENV: 'test'
}
})
test.before(async t => {
await app.isReady
})
test.after.always('cleanup', async t => {
await app.stop()
})