Testing javascript applications with Selenium, Async/Await, and Jest
Evan Tahler
Posted on November 3, 2019
Originally posted Mar 27, 2018
The last time I used Selenium, in 2015, I hated it. It was slow, brittle, and difficult to get working. These days, it can actually be pleasant!
Recently, in the ActionHero project, we found that we really needed a “full browser” integration test… something that we couldn’t mock or accomplish with even a robust tool like request. We needed to ensure that our HTTP and WebSocket libraries properly shared session & fingerprint information, which required cookies, headers, and 2 “full” protocols in the test… so we needed a real browser :/
We recently switched ActionHero’s test suite from mocha to Jest. Jest is an awesome test framework for javascript projects (and react, and other things that compile to javascript). It supports parallel testing, watching & retrying, mocking, snapshotting… all the tools I was missing coming from Rails, the gold-standard for TDD frameworks. It turns out that some wonderful person has already done the heavy lifting to make a full-featured integration between Selenium and Jest… and it’s actually simple to use!
What follows is a step-by-step guide to writing a “full-browser” test in Jest on OSX, complete with saving off photos of the page.
First, you’ll need to install a few things into your node.js project:
npm install --save-dev jest jest-environment-webdriver
# if you don't have homebrew: https://brew.sh/
brew install chromedriver
chromedriver
is a version of the Chrome browser which is able to be “machine controlled” by selenium in our tests. Note that we do not need to install anything else like the selenium server.
Jest already has support for multiple “renderers”. This is how it handles testing compiled-to-javascript files, like JSX. This means that we can signal to Jest in a given test file that it should use selenium. Jest uses magic comments for this:
/**
* @jest-environment jest-environment-webdriver
*/
The default is to use chromedriver
, which is what we’ll be using, but you can also test with Firefox, Safari, and other browsers. Using jest-environment-webdriver
means that we get a few new global variables we can use in our tests, specifically browser
, until
, and by
(full list here), which we will use in our test.
From here on out, you can use normal Jest commands to start your server in before blocks, configure whatever you need… and control your browser in the test. We can continue to use the normal Jest/Jasmine assertions
. In this example, we’ll be testing www.actionherojs.com
for a few things, but you’ll probably be testing localhost.
File Location: tests/integration/test.js
/**
* @jest-environment jest-environment-webdriver
*/
const url = 'https://www.actionherojs.com'
describe('www.actionherojs.com#index', () => {
test('it renders', async () => {
await browser.get(url)
const title = await browser.findElement(by.tagName('h2')).getText()
expect(title).toContain('reusable, scalable, and quick')
})
test('loads the latest version number from GitHub', async () => {
const foundAndLoadedCheck = async () => {
await until.elementLocated(by.id('latestRelease'))
const value = await browser.findElement(by.id('latestRelease')).getText()
return value !== '~'
}
await browser.wait(foundAndLoadedCheck, 3000)
const latestRelease = await browser.findElement(by.id('latestRelease')).getText()
expect(latestRelease).toEqual('v18.1.3')
})
describe('save a screenshot from the browser', () => {
test('save a picture', async () => {
// files saved in ./reports/screenshots by default
await browser.get(url)
await browser.takeScreenshot()
})
})
})
Your test can now be run via the normal jest
command. That’s it!
jest __tests__/integration/simple.js
PASS __tests__/integration/simple.js
www.actionherojs.com#index
✓ it renders (770ms)
✓ loads the latest version number from GitHub (267ms)
save a screenshot from the browser
✓ save a picture (784ms)
Test Suites: 1 passed, 1 total
Tests: 3 passed, 3 total
Snapshots: 0 total
Time: 3.204s, estimated 6s
Note that there is no need to start or stop the chromedriver
or selenium server (this handled for you).
Selenium is very powerful (full api docs here). You can type input, scroll the page, get and set cookies, etc. If you do find that you need a “full” integration test, this is a very painless way to do it!
Posted on November 3, 2019
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.