Aller au contenu principal

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

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:

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 :

wdio.conf.js
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 install --save-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 install --save-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:

test.js
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.

PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1 npm install --save-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 install --save-dev @playwright/test

:::attention pour les dépendances Ce didacticiel a été écrit playwright@1.16.3 et @playwright/test@1.16.3. Consultez la page de Playwright pour en savoir plus sur les changements qui pourraient affecter le code ci-dessous. :::

concernant l'utilisation de moteur d'exécution de test tiers: Si vous êtes intéressé à utiliser un exécuteur de test alternatif (e. . Jest ou Mocha), consultez le guide de Playwright Third-Party Test Runner. :::

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 :

example.spec.js
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)
A lire ultérieurieurement Consultez la documentation de Playwright pour l'intégralité des API de classe Electron et ElectronApplication. :::

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 :

testDriver.js
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:

main.js
// 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:

testDriver.js
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 :

main.js
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:

test.js
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()
})