Mocha JavaScript Tutorial: Selenium Testing Examples
Learn how to run Selenium browser tests with Mocha in JavaScript, including setup, hooks, waits, local and remote browsers, and troubleshooting.
Mocha organizes JavaScript tests; Selenium WebDriver drives a real browser; Node.js assertions check what the browser shows. Install both packages, create a Mocha test with a WebDriver in a setup hook, perform a user flow, assert its visible result, and always close the browser in a cleanup hook. This guide uses CommonJS, the module style shown in Selenium’s official Mocha example.
Use Node.js 22.12 or later for the overlap in the documented requirements: Mocha 12 requires ^20.19.0 || >=22.12.0, while Selenium’s JavaScript bindings require Node.js 22 or later. Check the packages’ current requirements when upgrading. Mocha Getting Started · Selenium JavaScript API.
What Mocha and Selenium each do
Mocha provides test structure and lifecycle hooks: describe groups tests, it defines a test, and before/after and their Each variants organize setup and cleanup. Selenium’s selenium-webdriver package controls the browser through WebDriver. Node’s built-in node:assert/strict module checks results without requiring a separate assertion library.
A useful browser test checks an outcome a user cares about, such as a confirmation message after submitting a form. A click completing without an error only proves the command ran; it does not prove the application completed the flow.
Install Mocha and Selenium
- Install Node.js 22.12 or later, then create a project if you do not already have one:
mkdir mocha-selenium-example cd mocha-selenium-example npm init -y - Install Mocha as a development dependency and Selenium’s JavaScript bindings:
npm install --save-dev mocha npm install selenium-webdriver - Create
test/form.spec.js. The example below opens Selenium’s public demo form, enters text, submits it, and checks the response.
Selenium Manager handles browser-driver installation in the basic JavaScript flow. You still need a compatible browser installed locally. A remote browser requires a reachable Selenium server, described later.
Complete runnable Mocha Selenium example
const { By, Builder } = require('selenium-webdriver');
const assert = require('node:assert/strict');
describe('Selenium form submission', function () {
let driver;
before(async function () {
driver = await new Builder().forBrowser('chrome').build();
await driver.manage().setTimeouts({
implicit: 0,
pageLoad: 30000,
script: 30000
});
});
after(async function () {
if (driver) {
await driver.quit();
}
});
it('shows a confirmation after submitting the form', async function () {
await driver.get('https://www.selenium.dev/selenium/web/web-form.html');
assert.equal(await driver.getTitle(), 'Web form');
await driver.findElement(By.name('my-text')).sendKeys('Selenium');
await driver.findElement(By.css('button')).click();
const message = await driver.findElement(By.id('message')).getText();
assert.equal(message, 'Received!');
});
});
The test uses By.name, By.css, and By.id to locate the example form’s input, button, and response. For your application, prefer locators tied to stable semantics or attributes maintained for automation; brittle selectors often fail when layout markup changes.
The example sets an explicit page-load timeout and script timeout, and disables implicit waiting. For dynamic elements, use explicit waits as shown below. Mixing implicit and explicit waits can make failure timing harder to reason about.
Run the test
Run the file through Mocha’s CLI from the project directory:
npx mocha test/form.spec.js
For a reusable command, add a script to package.json:
{
"scripts": {
"test:e2e": "mocha \"test/**/*.spec.js\" --timeout 60000"
}
}
Then run npm run test:e2e. Mocha’s default per-test timeout is often too short for a browser startup or slow remote environment; set an appropriate timeout rather than disabling it without a reason. Mocha’s own command examples include both mocha and npx mocha. See Selenium’s Mocha example and execution guidance.
Use Mocha hooks to manage browser lifecycle
beforeruns once before tests in its suite. Use it for a suite-level driver when sharing one browser session is acceptable.afterruns once after the suite. Call and awaitdriver.quit()here so browser processes are closed even if an assertion fails.beforeEachandafterEachrun around each test. Use them for test-specific navigation, state reset, or a fresh browser when isolation matters.
Sharing a driver starts the browser fewer times, but state can leak between tests through cookies, local storage, open tabs, or the current page. A fresh driver per test offers stronger isolation at the cost of more startup and teardown work. Pick the boundary that matches your app’s setup time and the failures you need to diagnose.
Mocha waits for returned promises, so mark tests and hooks async and await each WebDriver operation. Do not use Mocha’s callback-style done and an async return in the same test.
Wait for dynamic page state
Page navigation finishing does not guarantee that client-side rendering has added the element your test needs. Selenium’s waiting guidance explains that document readiness does not cover later JavaScript changes. Use a condition-based explicit wait for a specific outcome:
const { By, Builder, until } = require('selenium-webdriver');
// After building a driver and navigating to a page:
const result = await driver.wait(
until.elementLocated(By.id('results')),
10000,
'Results element did not appear within 10 seconds'
);
await driver.wait(until.elementIsVisible(result), 5000);
const text = await result.getText();
Choose a wait condition that describes readiness: element located, element visible, element enabled, or a URL/title change. Avoid arbitrary sleeps as the default; a fixed delay wastes time when the page is fast and can still be too short when it is slow. Selenium’s waiting strategies documentation describes why document readiness alone may not be enough.
Choose browser and assertion options
Browser choice
The example uses forBrowser('chrome'). Change the browser name to the browser configured in your environment, and ensure that browser is installed for local execution. Selenium Manager automates driver management in the documented JavaScript setup, but it does not install every browser or provision remote infrastructure.
Local or remote WebDriver
For Selenium Grid or a standalone Selenium server, point the builder at a server URL instead of starting a local browser:
const { Builder } = require('selenium-webdriver');
const driver = await new Builder()
.forBrowser('chrome')
.usingServer(process.env.SELENIUM_REMOTE_URL)
.build();
Set SELENIUM_REMOTE_URL to the reachable WebDriver endpoint for your environment. The remote server must be running and accessible to the test process. Selenium also documents remote configuration through Builder().usingServer(...) and the SELENIUM_REMOTE_URL environment variable. See the JavaScript API.
Assertions
The built-in strict assertion module is sufficient for basic checks:
const assert = require('node:assert/strict');
assert.equal(actualText, 'Expected text');
assert.ok(await element.isDisplayed());
Other assertion libraries can be used if the project already depends on one, but the core pattern is unchanged: await the browser observation, then assert on its value.
CommonJS and ESM
This article uses CommonJS consistently with Selenium’s official Mocha sample: require(...) and a conventional .js test file. If the project is configured for ES modules, use import statements and an ESM-compatible file or package configuration instead of mixing module systems in one file. Mocha’s current getting-started guide demonstrates ESM; follow the module convention already used by your project. See Mocha Getting Started.
Make the suite reliable and maintainable
- Assert meaningful state: check a visible message, URL, title, or other expected result after the action.
- Wait for conditions: let the page reach the state under test instead of relying on a short fixed sleep.
- Keep tests independent: reset data and browser state when one test could affect another.
- Always clean up: quit the driver in an awaited
afterhook, and ensure every driver you create has a corresponding quit. - Keep selectors understandable: centralize repeated selectors when appropriate and avoid depending on incidental visual structure.
- Use timeouts deliberately: make page-load, script, wait, and Mocha test timeouts long enough for the environment, but finite so hangs become actionable failures.
For a small suite, a shared browser can reduce repeated startup. For tests where isolation is more important, create a driver per test and quit it in afterEach. Neither pattern is universally faster: startup cost, remote session allocation, and test independence vary by environment.
Performance, reliability, and cost considerations
Browser tests are slower and more resource-intensive than checks that do not launch a browser because they start a browser session and interact with a rendered page. Avoid repeating browser startup unnecessarily, but do not share state in a way that makes failures order-dependent. Parallel execution can increase total throughput only when each test has isolated state and enough browser capacity; the supplied sources do not provide benchmark comparisons.
Local runs avoid remote server provisioning but depend on the machine’s installed browser and resources. Remote runs centralize browser execution, but add network and server availability as dependencies. A remote endpoint that cannot be reached, a saturated Grid, or an unavailable browser node should be investigated as infrastructure failure rather than assumed to be an application assertion failure.
There is no fixed Selenium cost stated here: the library is installed as a package, while browser machines or hosted Grid capacity depend on how you run it. Account for browser workers, CI minutes, and any remote execution service separately. No speed or cost benchmark is implied.
Troubleshooting common Mocha and Selenium failures
| Symptom | Likely cause | What to do |
|---|---|---|
describe is not defined or no tests are discovered |
The file was run directly with Node, or the file path/pattern does not match Mocha’s discovery. | Run it with npx mocha test/form.spec.js and check the test file path and extension. |
| Mocha rejects the Node version | The active Node version does not satisfy Mocha’s supported range. | Use a documented compatible Node version. For Mocha 12 and Selenium together, Node 22.12 or later satisfies the overlapping stated requirements. |
| Browser or driver startup fails | The browser is missing, incompatible, or Selenium Manager cannot complete driver setup in the environment. | Confirm the selected browser is installed and can run in the environment; check network or environment restrictions affecting driver management; inspect the startup error before changing browser configuration. |
| Remote session cannot be created | The Selenium server URL is unset, malformed, unreachable, or has no available browser capacity. | Check SELENIUM_REMOTE_URL, server reachability, and remote Grid health; verify the requested browser is available on that server. |
NoSuchElementError |
The locator is wrong, the page differs from expectation, or the element has not appeared yet. | Inspect the current page and selector, then wait for the relevant element or page state before interacting. |
| Click succeeds but the assertion fails | The application did not reach the expected state, or the test read it before asynchronous work completed. | Wait for the expected message, URL, or other result; verify the form input and submission behavior independently. |
| Test times out | The test exceeded Mocha’s timeout, a browser command is waiting, or the page is blocked or slow. | Identify which operation is slow, set an appropriate finite timeout, and add a specific wait condition. Avoid masking a stuck command by disabling timeouts. |
| Browser processes remain after failures | Driver cleanup did not run or the quit promise was not awaited. | Use an awaited after/afterEach hook and call driver.quit() for every created driver. |
| Tests pass alone but fail in a suite | Tests share cookies, local storage, application data, or browser tabs. | Reset state between tests or create isolated driver sessions; avoid relying on test order. |
| Syntax or module loading error | CommonJS require and ESM import conventions are mixed or conflict with package settings. |
Use one module style that matches the project configuration and Mocha invocation. |
Or skip the browser setup
If the task is to capture a page image or PDF rather than verify an interactive flow, ScreenshotNeo can return a screenshot or PDF with one API request. Its API documentation describes the request options.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
await require('node:fs/promises').writeFile('shot.webp', Buffer.from(await res.arrayBuffer()));
ScreenshotNeo removes cookie banners, newsletter popups, and chat widgets before capture. Bot checks, blank pages, and failed loads are never billed. An MCP server lets AI agents use screenshot tools. The free plan includes 1,000 screenshots a month with no card, and paid plans start at $5 for 3,000. It is a capture service, so use Selenium when the requirement is to exercise and assert an interactive browser flow. Learn about ScreenshotNeo.
Sign up for 1,000 free screenshots a month, with no card required.
FAQ
Does Mocha control the browser?
No. Mocha runs and organizes the tests; Selenium WebDriver sends browser commands.
Can I use this pattern for Firefox or another browser?
Yes. Select the browser configured in your environment with the builder and make sure it is available locally or on the remote Selenium server.
Should every test get its own browser?
Use a separate session when test isolation is important. A suite-level driver can reduce repeated startup, but requires deliberate state cleanup.
Can ScreenshotNeo replace Selenium for end-to-end testing?
No. It captures pages as images or PDFs. Selenium is the fit for interacting with a browser and asserting application behavior.


