J’ai déjà parlé dans des articles précédents de Protractor (ici et là), l’outil qui permet de faire des tests d’interface, end-to-end (E2E).
Je vais vous présenter ici Page Objects, un design pattern afin de faciliter le développement de vos tests. Page Objects permet :
- d’avoir un code plus lisible
- d’éviter la duplication de code
- d’éviter d’avoir un couplage fort entre votre test et la page à tester
- de faciliter la maintenance de vos tests
Une page object est une classe qui va servir d’interface entre la page à tester et vos tests. Ainsi, le test n’a pas à interagir avec votre interface mais passe par la classe « Page Object ». Ainsi, si votre interface change, seulement le code de votre page objet sera à modifier et non le test, facilitant ainsi la maintenance. La page object va exposer des méthodes fonctionnelles qui vont rendre plus lisible vos tests.
Exemple
Voici un exemple simple concernant une page de login.
Voici le test sans utilisation de page objects.
describe('Login', function () { var ptor, loginInput, passInput, submitBtn, error; beforeEach(function () { ptor = protractor.getInstance(); loginInput = by.model('username'); passInput = by.model('password'); submitBtn = by.css('[type=submit]'); error = by.binding('error'); }); it('should display error if login password are wrong', function () { element(loginInput).sendKeys('wrongUser'); element(passInput).sendKeys('pass123'); element(submitBtn).click(); browser.waitForAngular(); expect(ptor.getCurrentUrl()).not.toMatch(/\/home/); expect(element(error).getText()).not.toEqual(''); }); it('should redirect to home page is login/pass are correct', function () { element(loginInput).sendKeys('admin'); element(passInput).sendKeys('123456'); element(submitBtn).click(); browser.waitForAngular(); expect(ptor.getCurrentUrl()).toMatch(/\/home/);; }); });
Voici comment améliorer nos tests avec Page Object. Dans le cas de protractor, les tests sont exécutés via node js. Nous allons créer un module node qui pourra ainsi être chargés dans nos tests.
La page Object de la page de login :
'use strict'; var LoginPage = function () { browser.get('http://mySiteUrl'); }; LoginPage.prototype = Object.create({}, { loginText: { get: function () { return element(by.model('username')); }}, passwordText: { get: function () { return element(by.model('password')); }}, loginButton: { get: function () { return element(by.css('input[type="submit"]')); }}, error = { get: function () { return element(by.binding('error')).getText(); }}, login: { value: function (login, password) { this.loginText.sendKeys(login); this.passwordText.sendKeys(password); this.loginButton.click(); browser.waitForAngular(); }} }); module.exports = LoginPage;
Le test devient :
//On charge le module var LoginPage = require('../pages/login.page.js'); describe('Login', function () { var page, ptor; beforeEach(function () { //avant chaque test on on récupère la page page = new LoginPage(); ptor = protractor.getInstance(); }); it('should display error if login password are wrong', function () { page.login('wrongUser', 'pass'); expect(ptor.getCurrentUrl()).not.toMatch(/\/home/); expect(page.error).not.toEqual(''); }); it('should redirect to project home page is login/pass are correct', function () { page.login('julien', 'azerty123'); expect(ptor.getCurrentUrl()).toMatch(/\/home/); }); });
On peut voir que le test est beaucoup plus lisible et maintenable.
De plus, il devient facile de réutiliser notre code. En effet, si on doit tester des pages ou l’on doit être identifié, on peut facilement réutiliser la page objects de login afin de se connecter avant chaque test.
J’ai mis à jour les tests E2E de mon application de volley. Le code est disponible sur github.
Références
Bon tests 😉
Tweet