ScreenshotNeo

BlogHow-to

How to Use Selenium with Python and Nose2

Set up Selenium browser tests with nose2, configure test discovery, wait reliably for dynamic pages, and troubleshoot common WebDriver issues.

By the ScreenshotNeo team4 October 20266 min read

Selenium drives a real browser; nose2 discovers and runs your Python tests. Install both packages in a project virtual environment, put Selenium checks in tests nose2 can discover, and run nose2 from the project directory. For JavaScript-driven pages, wait for the specific page state your test needs instead of relying on a fixed delay.

1. Understand what Selenium and nose2 do

Selenium WebDriver is the browser automation layer: your Python code asks a browser to navigate, locate elements, click, and expose page state. A browser-specific driver connects WebDriver to the browser. nose2 is the test runner and discovery layer; it is based on Python’s unittest and adds discovery and plugins. In short, Selenium operates the browser; nose2 finds and runs the tests. See the official Selenium documentation and nose2 documentation.

2. Install the packages

Use a virtual environment so the test dependencies are isolated from other projects. From the project root:

python -m venv .venv
# macOS or Linux
source .venv/bin/activate
# Windows PowerShell: .venv\Scripts\Activate.ps1
python -m pip install --upgrade pip
python -m pip install -U selenium nose2

Run nose2 after creating a test. The nose2 getting-started guide documents pip install nose2; Selenium’s Python API documentation gives pip install -U selenium. Check your project’s supported Python and browser versions before pinning versions. Modern Selenium includes Selenium Manager, which generally handles driver setup for supported browsers and platforms, so manual driver downloads are usually unnecessary. Consult the Selenium Manager documentation if setup fails.

3. Write and run a discoverable browser test

By default, nose2 discovers Python modules whose filenames begin with test, then recognizes unittest.TestCase methods and test functions beginning with test. Name a module test_example_page.py, for example, and place it where discovery starts.

import unittest
from selenium import webdriver
from selenium.webdriver.common.by import By


class TestExamplePage(unittest.TestCase):
    def setUp(self):
        self.driver = webdriver.Chrome()

    def tearDown(self):
        self.driver.quit()

    def test_page_has_heading(self):
        self.driver.get("https://example.com")
        heading = self.driver.find_element(By.TAG_NAME, "h1")
        self.assertTrue(heading.text)

Run the suite from the directory containing the project:

nose2

This example demonstrates the test structure and Selenium API; it does not assert what any particular live site will display. Always close the browser in teardown, including when a test fails, so browser processes do not accumulate.

4. Wait for dynamic content reliably

A navigation call returning does not necessarily mean JavaScript-rendered content is ready. Prefer an explicit wait for the exact condition the test depends on:

from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import WebDriverWait

heading = WebDriverWait(self.driver, 10).until(
    EC.presence_of_element_located((By.TAG_NAME, "h1"))
)
self.assertTrue(heading.text)

The timeout is a maximum: the wait continues until the condition is met or the deadline expires. Use a condition that matches the assertion. For example, presence means the element exists in the DOM; visibility is more appropriate when the test needs to interact with a visible element. Selenium’s waits documentation describes synchronization options.

Approach What it does When to use it
Explicit wait Polls for a named condition at a particular point. Preferred for dynamic pages because the wait states what the test needs.
Implicit wait Applies a polling timeout to element lookups. Can be set globally, but avoid mixing it casually with explicit waits because combined wait behavior can be difficult to reason about.
Fixed sleep Always pauses for a set duration. Use only when a deliberate pause itself matters; otherwise it wastes time on fast runs and may still be too short on slow ones.

5. Configure nose2 discovery and select tests

If default discovery does not match the repository structure, set the start directory and import root explicitly:

nose2 -s tests -t .

-s (or --start-dir) chooses where discovery begins; -t (or --top-level-directory) sets the importable project root. You can also select a dotted module, class, or individual test, for example:

nose2 tests.test_example_page
nose2 tests.test_example_page.TestExamplePage
nose2 tests.test_example_page.TestExamplePage.test_page_has_heading

For repeatable project settings, nose2 supports configuration files such as unittest.cfg and nose2.cfg. A minimal example is:

[nose2]
start-dir = tests

Configuration can also set code directories, file patterns, and plugins. Discovery and loading are plugin-based; --no-plugins disables normal discovery and loading, so do not add it as a generic troubleshooting flag. Review the nose2 usage guide and configuration guide for supported settings.

6. Run tests locally, then scale execution

A local WebDriver session is the simplest starting point and gives direct access to the browser installed in that environment. For broader browser and environment coverage or distributed execution, Selenium documents Selenium Grid as the scaling path. Grid adds infrastructure and configuration, so begin locally and move execution only when the project’s coverage or throughput needs justify operating remote browser sessions. See the official Selenium Grid documentation.

Keep tests independent, use explicit waits, and avoid unnecessary browser startups when suite time becomes a problem. Browser automation is affected by machine load, network latency, remote site behavior, and browser versions; report these separately from application assertion failures where possible.

7. Troubleshoot common failures

Symptom Likely cause Fix
nose2 reports no tests The filename does not start with test, test names do not start with test, or discovery starts in the wrong directory. Rename the module and test methods to match the defaults, run from the project root, or set -s and -t.
Import error for a project module The project root is not on the import path or the selected top-level directory is wrong. Set -t to the importable root, check package layout, and run nose2 from the intended environment.
WebDriver cannot start or browser driver is missing The browser is absent, unsupported, mismatched, or Selenium Manager cannot obtain/configure its driver in the environment. Confirm the browser is installed and supported, check Selenium’s version and Selenium Manager guidance, and inspect network or managed-environment restrictions.
NoSuchElementException The locator is wrong, the element is in a different frame, or it has not appeared yet. Verify the locator and frame context; wait for the relevant condition before looking up the element.
TimeoutException The expected state did not occur before the maximum wait, or the condition is too strict. Inspect the page and locator, choose the appropriate condition, and increase the timeout only if the application legitimately needs more time.
Browser process remains after a failed test Cleanup did not run or the browser was never quit. Put quit() in teardown or a cleanup hook guaranteed to run after failure.
Test passes alone but fails in the suite Tests share state, depend on ordering, or leave browser/session state behind. Make setup and cleanup per test, isolate data, and avoid ordering assumptions.

8. Capture screenshots from tests without managing a browser

If the task is to save a page image rather than assert browser behavior, Selenium may be more setup than needed. ScreenshotNeo is a website screenshot API and MCP server from Yorker Media. Its one-call API returns an image or PDF, and its parameter names are compatible with those used by other screenshot APIs. See ScreenshotNeo and the ScreenshotNeo API documentation.

Here is the cURL form:

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp

Python:

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)

Node.js:

const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);

Or skip the browser setup

ScreenshotNeo accepts cookie and consent banners before capture and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each cleanup step can be turned off. Bot checks, blank pages, failed loads, timeouts, and cache hits are not billed, and response headers identify the page verdict and billing status. An MCP server provides take_screenshot, get_page_info, and capture_pdf for Claude, Cursor, and other MCP clients. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000 screenshots. Every feature is on every plan. Use the call above to try the API, then sign up for 1,000 free screenshots a month with no card.

9. Frequently asked questions

Do I need to install ChromeDriver separately?

Usually not with current Selenium on supported platforms: Selenium Manager generally handles driver setup. Check its documentation for your browser and environment.

Can I use nose2 with Selenium tests written as functions?

nose2 recognizes test functions named with the test prefix, as well as unittest.TestCase methods. Keep the browser lifecycle explicit so sessions are closed reliably.

Should I use Selenium for every screenshot?

Use Selenium when the test must interact with a browser or verify browser behavior. For a page image or PDF without browser-test assertions, a screenshot API can avoid local browser and driver setup.