Tests automatisés
L'automatisation des tests est un moyen efficace de valider que le code de votre application fonctionne comme prévu. Bien qu'Electron ne maintienne pas activement sa propre solution de test, ce guide va présenter deux façons de faire des tests automatisés de bout en bout sur votre application Electron.
En utilisant l’interface WebDriver
Extrait de: ChromeDriver - WebDriver pour Chrome :
WebDriver est un outil open source pour les tests automatisés d'applications web sur plusieurs navigateurs. Il fournit des fonctions pour naviguer vers les pages web, simuler l'input utilisateur, l’exécution de JavaScript, etc... . ChromeDriver est un serveur autonome qui implémente le protocole de WebDriver (WebDriver's wire protocol) pour Chromium. Il est développé par des membres des équipes chrome et WebDriver.
Il y a plusieurs façons de configurer les tests en utilisant WebDriver.
Avec WebdriverIO
WebdriverIO (WDIO) est un framework d'automatisation de test fournissant un package Node.js pour tester avec WebDriver. Son écosystème comprend également divers plugins (par exemple, reporter et services) qui peuvent vous aider à assembler votre configuration de test.
Installation du testrunner
Vous devez d'abord exécuter le toolkit de démarrage WebdriverIO dans le répertoire racine de votre projet:
- npm
- Yarn
npx wdio . --yes
npx wdio . --yes
Cela installe pour vous tous les packages nécessaires et génère un fichier de configuration wdio.conf.js
.
Connexion de WDIO à votre application Electron
Mettez à jour la propriété "capabilities" de votre fichier de configuration pour pointer vers le binaire de votre application Electron :
export.config = {
// ...
capabilities: [{
browserName: 'chrome',
'goog:chromeOptions': {
binary: '/path/to/your/electron/binary', // Path to your Electron binary.
args: [/* cli arguments */] // Optional, perhaps 'app=' + /path/to/your/app/
}
}]
// ...
}
Lancement des tests
Pour exécuter les tests:
$ npx wdio run wdio.conf.js
Avec sélénium
Selenium est un framework d’automatisation Web exposant des liaisons aux API WebDriver dans de nombreux languages. Les liaisons Node.js sont disponibles dans le package selenium-webdriver
sur NPM.
Démarrage d'un serveur ChromeDriver
Afin d'utiliser Selenium avec Electron, vous devez télécharger le binaire electron-chromedriver
et le lancer :
- 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.
N'oubliez pas le numéro du port 9515
, qui servira plus tard.
Connexion de Selenium à ChromeDriver
Installez Selenium dans votre projet :
- npm
- Yarn
npm install --save-dev selenium-webdriver
yarn add --dev selenium-webdriver
Usage of selenium-webdriver
with Electron is the same as with normal websites, except that you have to manually specify how to connect ChromeDriver and where to find the binary of your Electron app:
const webdriver = require('selenium-webdriver')
const driver = new webdriver.Builder()
// The "9515" is the port opened by 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()
Utilisation de Playwright
Microsoft Playwright est un framework de test utilisant les protocoles de débogage à distance spécifiques au navigateur, similaire à l’API Node.js sans affichage Puppeteer, mais orienté vers les tests de bout en bout. Playwright prend en charge expérimentalement Electron via la prise en charge par Electron du protocole Chrome DevTools (CDP).
Installer les dépendances
Vous pouvez installer Playwright via votre gestionnaire de packages Node.js préféré. L'équipe Playwright recommande d'utiliser la variable d'environnement PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD
pour éviter les téléchargements inutiles de navigateur lors du test d'une application Electron.
- npm
- Yarn
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1 npm install --save-dev playwright
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1 yarn add --dev playwright
Playwright est également livré avec son propre moteur d'exécution de test, Playwright Test, qui est conçu pour les tests de bout en bout . Vous pouvez également l'installer en tant que dépendance de dev dans votre projet :
- npm
- Yarn
npm install --save-dev @playwright/test
yarn add --dev @playwright/test
This tutorial was written playwright@1.16.3
and @playwright/test@1.16.3
. Consultez la page de Playwright pour en savoir plus sur les changements qui pourraient affecter le code ci-dessous.
If you're interested in using an alternative test runner (e.g. Jest or Mocha), check out Playwright's Third-Party Test Runner guide.
Rédaction des tests
Playwright lance votre application en mode développement via l'API _electron.launch
. Pour faire pointer cette API vers votre application Electron, vous pouvez passer le chemin vers votre point d'entrée de processus principal (ici, c'est 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()
})
Après cela, vous aurez accès à une instance de la classe ElectronApp
de Playwright. Cette classe est une classe puissante ayant accès aux modules du processus principal par exemple:
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 }) => {
// Cela s'exécute dans le processus principal d'Electron, le parmètre est toujours
// le résultat du require('electron') dans le script main de l'application.
return app.isPackaged
})
console.log(isPackaged) // false (parce que nous sommes en mode développement)
// close app
wait electronApp.close()
})
Il peut également créer des objets Page individuels à partir d’instances Electron BrowserWindow. Par exemple, pour capturer la premiere BrowserWindow et enregistrer la capture d’écran :
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()
})
En utilisant tout cela avec PlayWright Test runner, créons un fichier de test example.spec.js
avec un seul test et une seule assertion :
const { _electron: electron } = require('playwright')
const { test, expect } = require('@playwright/test')
test('example test', async () => {
const electronApp = await electron.launch({ args: ['.'] })
const isPackaged = attendre electronApp. valuate(async ({ app }) => {
// Cela s'exécute dans le processus principal d'Electron, le paramètre ici est toujours
// le résultat du require('electron') dans le script principal de l'application.
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()
});
Ensuite, exécutez Playwright Test à l’aide de npx playwright test
. Vous devriez voir la réussite du test dans votre console et avoir une capture d’écran intro.png
enregistrée dans le système de fichiers.
☁ $ npx playwright test
Exécute 1 test en utilisant 1 worker
✓ example.spec.js:4:1 › exemple test (1s)
Playwright Test will automatically run any files matching the .*(test|spec)\.(js|ts|mjs)
regex. Vous pouvez personnaliser cette correspondance dans les options de configuration de Playwright Test.
Check out Playwright's documentation for the full Electron and ElectronApplication class APIs.
Utiliser un pilote de test personnalisé
Il est également possible d'écrire votre propre driver personnalisé avec l'IPC-over-STDIO intégré à Node.js. Les pilotes de test personnalisés exigent que vous écriviez du code d'application supplémentaire, mais nécessite moins de code et vous permettre d'exposer des méthodes personnalisées à votre suite de test.
Pour créer un pilote personnalisé, nous utiliserons l’API child_process
de Node.js. La suite de tests fera apparaître le processus Electron, puis établira un protocole de messagerie basique :
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) => {
// ...
})
// send an IPC message to the app
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' })
We can now communicate from the test suite to the Electron app using the appProcess
object.
For convenience, you may want to wrap appProcess
in a driver object that provides more high-level functions. Voici un exemple de la façon dont vous pouvez le faire. 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 };
Dans votre code d'application, vous pouvez alors écrire un gestionnaire simple pour recevoir des appels RPC :
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()
})