TESTER SON JAVASCRIPT*
DANS LA JOIE ET LA BONNE HUMEUR
π₯π₯π₯
*FRONT-END

Nicolas Payot
Lead Front-End Developer [ DawexΒ ]



FRONT-END APPS ARE MORE AND MORE COMPLICATED π£

THE MORE COMPLICATED, THE MORE AUTOMATED TESTS WE NEED
βοΈβοΈβοΈ

TESTS SHOULD NOT BE A BURDEN TO WRITE!
OTHERWISE WE DON'T WRITE THEM... π€

TESTING EXPERIENCE IS BETTER THANKS TO MODERN FRAMEWORKS

MOCHA
CHAI
SINON
KARMA
PHANTOMJS
JASMINE
TAPE
AVA
ENZYME
JSDOM
ISTANBUL
JEST π
NOWADAYS

UNIT TESTS AT
JASMINE / KARMA / PHANTOMJS
JEST
πππ


Delightful JavaScript Testing π
MEET JEST


GETTING STARTED WITH JEST
npm install --save-dev jestyarn add --dev jest*.test.js
*.spec.js
__tests__/**/*.js
jest
ASSERTIONS WITH JEST

π΄
expect(something)
.toBe(value)
.toEqual(value)
.toContain(item)
.toMatch(regexOrString)
.toHaveProperty(string)
...
.not.toBe / toEqual / ...AND
CUSTOM
MATCHERS

ERROR FEEDBACK
HERE'S WHAT YOU GOT
BUT HERE'S WHAT YOU EXPECTED


TESTING ASYNC CODE IS YAY!
Callbacks, Promises, async / await
π€

jest --watchINTERACTIVE WATCH MODE π
RUNS TESTS ONLY ON CHANGED FILES SINCE LAST COMMIT

INTERACTIVE WATCH MODE π


MANUAL MOCKS
api.js __mocks__/api.js
jest.mock('./path/to/api');
// imported api is mocked version
import { api } from './path/to/api';
SNAPSHOT TESTING πΈ
expect(serializedComponent)
.toMatchSnapshot();HelloWorld.vue HelloWorld.spec.js __snapshots__/HelloWorld.spec.js.snap

BUILT-IN CODE COVERAGE
AND ALSO...
PERFORMANCES πͺπͺπͺ
TESTS RUN IN PARALLEL BY DEFAULT

DEMO TIME
π€

NOW, LET'S TALK ABOUT E2E TESTING
π€

SELENIUM WEBDRIVER API
π

JavaScript code
(Tests / WebDriver client API)
ChromeDriver
HTTP server
Commands to control Chrome
(WebDriver protocol)

SELENIUM 2.0

BUT SELENIUM...
π HARD TO DEBUG
π FLAKY TESTS
π

SELENIUM FRIENDS
(AND COMPETITORS)
WEBDRIVER.IO
CYPRESS π
PUPPETEER π
PROTRACTOR
NIGHTWATCH
CASPERJS


Fast, easy and reliable testing for anything that runs in a browser
MEET CYPRESS

E2E TESTS AT
PROTRACTOR / CUCUMBERJS π₯
CYPRESS
πππ


NO DEPENDENCY
DIRECTLY RUNS IN THE BROWSER
DESKTOP GUI (REACT, ELECTRON)
RUNS IN CI ENVIRONMENTS
VIDEO RECORDS (CI)

GETTING STARTED WITH CYPRESS
npm install --save-dev cypressyarn add --dev cypresscypress opencypress runCYPRESS SCAFFOLDING

CYPRESS API
describe()
it()
beforeEach()
.only()
.skip()
// ...MOCHA
cy.stub()
cy.spy()SINON
CHAI
expect(something)
.to.be.true
.to.equal(value)
.to.contain(value)
// ...
INTERACTING WITH DOM
cy.get('selector').find(node)
cy.get('nav a').first()
cy.get('nav').children()
cy.get('nav').parents()
// ...SELECTORS

INTERACTING WITH DOM
.type('something') // {enter}, {backspace}
.click()
.dblclick()
.check() / .uncheck()
.select()
.focus() / .blur()
// ...EVENTS

cy.get('#header a')
.should('have.class', 'active')
.and('have.attr', 'href', '/users');ASSERTIONS WITH CYPRESS
IMPLICIT SUBJECTS
Pretty handy, right?

cy.get('#header a')
.should($a => {
// perform some logic before assertions
expect($a).to.have.class('active');
expect($a).to.have.attr('href', '/users');
});ASSERTIONS WITH CYPRESS
EXPLICIT SUBJECTS
A bit less handy...

USE CUSTOM COMMANDS FOR BETTER CODE
Cypress.Commands.add('login', (username, password) => {
// command logic
});
it('should login', () => {
cy.login('john', '1234');
// ...
});
WAIT FOR XHR REQUESTS
// ...
cy.route('POST', '/api/login').as('login');
cy.get('button[type="submit"]')
.click();
cy.wait('@login');PREVENTS TEST FLAKE

SPY / STUB XHR REQUESTS
cy.route({
method: 'POST',
url: '/checkout',
response: {
items: ['product1', 'product2']
}
});PREVENTS TEST FLAKE, ALSO.

DEBUGGING?
LOGS FROM RUNNER
debugger / .debug()
BROWSER DEVTOOLS

DEMO TIME
π€π€

THANK YOU!


RESOURCES
Tester son JavaScript dans la joie et la bonne humeur [LyonJS]
By Nicolas Payot
Tester son JavaScript dans la joie et la bonne humeur [LyonJS]
- 1,639