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

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 lanceur de tests

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

L'utilisation de selenium-webdriver avec Electron est pratiquement la même qu’avec les sites Web normaux, la différence est que vous devez spécifier manuellement comment connecter ChromeDriver et où trouver le binaire de votre application Electron:

test.js
const webdriver = require('selenium-webdriver')
const driver = new webdriver.Builder()
// "9515" est le port ouvert par le 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()

En utilisant Playwright

Microsoft Playwright est un framework de test complet utilisant les protocoles de débogage à distance spécifiques au navigateur, similaire à l’API sans affichage Puppeteer, mais orienté vers une solution globale de test. Playwright prend en charge expérimentalement Electron via la prise en charge par Electron du protocole Chrome DevTools (CDP).

Installation des 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 mener 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
Concernant les Dépendances

Ce tutoriel a été écrit pour [email protected] et @playwright/[email protected]. Consultez la documentation sur les versions de Playwright pour en savoir plus sur les changements qui pourraient affecter le code ci-dessous.

Utilisation de lanceurs de tests tiers

Si vous souhaitez utiliser un lanceur de tests alternatif (par exemple, Jest ou Mocha), consultez le guide Third-Party Test Runner de Playwright.

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 devez fournir en argument le chemin du point d'entrée de votre processus principal (ici, il s'agira de 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()
})

Vous obtiendrez alors une instance de la classe ElectronApp de Playwright. Il s'agit la d'une classe aux fonctionnalités puissantes ayant accès aux modules du processus principal comme dans l'exemple suivant:

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()
})

Cette classe peut également créer des objets Page individuels à partir d’instances de BrowserWindow d'Electron . Par exemple, comme ci-dessous, pour capturer la premiere BrowserWindow et enregistrer une 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 recourant à toutes ces notions, utilisons maintenant le lanceur de test PlayWright Test et créons un fichier de test nommé example.spec.js qui contient 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()
});

Il faut maintenat exécuter Playwright Test à l’aide de npx playwright test. Vous devez 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)
info

Playwright Test exécutera automatiquement tous les fichiers satisfaisant a la regex .*(test|spec)\.(js|ts|mjs) . Vous pouvez personnaliser l'expression régulière dans les options de configuration comme indiqué dans la documentation correspondante de Playwright Test.

Lectures complémentaires

Consultez la documentation de Playwright concernant la classe d' interfaçage avec Electron et les Apis concernant l'application Electron .

En utilisant un pilote de test personnalisé

Il est également possible d'écrire son propre driver personnalisé à l'aide de 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écessitent moins de code et vous permettent 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')

// création d'un processus enfant
const env = { /* ... */ }
const stdio = ['inherit', 'inherit', 'inherit', 'ipc']
const appProcess = childProcess.spawn(electronPath, ['./app'], { stdio, env })

// écoute des messages IPC de l'application
appProcess.on('message', (msg) => {
// ...
})

// envoi d'un message IPC à l'application
appProcess.send({ my: 'message' })

Vous pouvez écouter des messages et envoyer des réponses à l’aide de l’API Node.js process depuis l’application Electron, :

main.js
// écoute des messages provenant de la suite de test
process.on('message', (msg) => {
// ...
})

// envoi d'un message à la suite de test
process.send({ my: 'message' })

Nous pouvons maintenant faire communiquer la suite de tests et l’application Electron à l’aide de l’objet appProcess .

Pour plus de commodité, vous pouvez encapsuler appProcess dans un objet testeur qui fournira des fonctions de plus haut niveau. Voici un exemple de la façon dont vous pouvez le faire. Commençons par créer la classe TestDriver :

testDriver.js
class TestDriver {
constructor ({ path, args, env }) {
this.rpcCalls = []

// demarrage du processus enfant
env.APP_TEST_DRIVER = 1 // fait savoir à l'application
// qu'elle doit être à l'écoute de messages
this.process = childProcess.spawn(path, args, { stdio: ['inherit', 'inherit', 'inherit', 'ipc'], env })

// gestion des réponses rpc
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)
})
}

// appel RPC simple
// 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)
}

Maintenant vous pourrez utiliser votre classe de TestDriver avec le framework d’automatisation de tests de votre choix. L’exemple suivant utilise ava, mais d'autres frameworks comme Jest ou Mocha fonctionneront également :

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()
})