How to Use Source Control for Selenium Test Projects
Make Selenium tests reproducible for your team: version the right files, document setup and run commands, organize page objects, and handle test data deliberately.
Use Git to version the Selenium test code, dependency and runner configuration, and the instructions contributors need to run the suite. Keep credentials and environment-specific secrets out of the repository. A new contributor should be able to clone the project, install its dependencies, configure any required test data, and run a documented test command.
Selenium does not prescribe one repository layout or Git workflow. Its bindings support multiple languages and runners, and setup varies with the project. Selenium Manager is the default browser and driver management mechanism in current Selenium bindings, though a team may have environment-specific constraints. See Selenium’s documentation and its guide to organizing and executing Selenium code.
1. Decide what belongs in source control
Commit the files needed to understand, install, and run the project. A typical repository includes:
- Test source files and reusable page objects or page components.
- Dependency manifests and lockfiles where the language’s package manager uses them.
- Runner configuration, such as Maven or Gradle configuration, pytest configuration, or .NET project files.
- Non-secret configuration examples, such as an environment-variable template with placeholder values.
- Contributor instructions, test fixtures that are safe and appropriate to share, and scripts that make setup or execution repeatable.
Keep generated reports, local caches, virtual environments, browser profiles, and machine-specific output out of commits unless the team has a specific reason to track them. The exact ignore list depends on the language, runner, and project; Selenium does not define a universal .gitignore.
Example repository layout
selenium-project/
├── README.md
├── .gitignore
├── .env.example
├── pom.xml # or build.gradle, pyproject.toml, *.csproj, etc.
├── src/
│ ├── test/
│ │ └── ... # layout depends on language and runner
│ └── ...
├── tests/ # common in some language and runner setups
├── pages/ # optional: page objects or page components
├── fixtures/ # safe, deterministic test data
└── scripts/ # optional setup or test helpers
This is an example, not a Selenium standard. Choose one layout that fits the team’s stack and explain it in the README. Avoid keeping two competing test roots or undocumented copies of the same fixture.
2. Make setup and test commands repeatable
Document the language runtime, dependency installation command, browser expectations, configuration variables, and the normal test command. Selenium’s examples cover cloning, installing dependencies, and executing tests with runner-specific commands including mvn clean test, gradle clean test, and pytest. Use the command for your actual project, not a generic Selenium command.
Python with pytest
For a Python project whose dependencies are listed in requirements.txt, a basic local workflow can look like this. The test file and fixtures are project-specific; the commands assume they already exist.
git clone https://example.invalid/team/selenium-project.git
cd selenium-project
python -m venv .venv
# macOS or Linux:
. .venv/bin/activate
# Windows PowerShell: .venv\Scripts\Activate.ps1
python -m pip install -r requirements.txt
pytest
Replace the clone URL with the repository’s real address before using this example. If the project uses a different dependency manager, document that manager’s install and lockfile workflow instead.
# Run one test file
pytest tests/test_checkout.py
# Run one test by name
pytest tests/test_checkout.py -k test_guest_can_complete_checkout
Java with Maven
git clone https://example.invalid/team/selenium-project.git
cd selenium-project
mvn test
# Run a clean test build
mvn clean test
Use the project’s checked-in pom.xml and document any required profiles or system properties. To run a particular test, follow the test framework and Maven configuration used by that repository; class and method selector syntax depends on the configured provider.
Java with Gradle
git clone https://example.invalid/team/selenium-project.git
cd selenium-project
./gradlew test
# Run a clean test build
./gradlew clean test
On Windows, use the wrapper script supplied by the repository, typically gradlew.bat. If the project uses a system-installed Gradle rather than the wrapper, say so and specify the expected version. The wrapper can make the Gradle version used by contributors explicit.
.NET
git clone https://example.invalid/team/selenium-project.git
cd selenium-project
dotnet restore
dotnet test
These examples show the shape of a clone-install-run workflow, not interchangeable project templates. Follow the Selenium documentation and the chosen runner’s documentation for exact setup and test selection syntax.
Write a useful README
At minimum, answer these questions in the repository’s contributor instructions:
- Which language runtime and version should contributors use?
- How do they install dependencies, and should they use a lockfile or wrapper?
- Which browser is supported, and how are the browser and driver obtained in this environment?
- Which environment variables or test accounts are required, and how can a contributor obtain safe values?
- What command runs the full suite, and what command runs one test or test file?
- Where are failures, logs, screenshots, or reports written?
Current Selenium bindings use Selenium Manager by default to manage browser and driver setup. That reduces setup work in many environments, but does not remove the need to document browser requirements, network access, pinned versions, or other local and CI constraints when they apply.
3. Separate test intent from page mechanics
Keep each test focused on a user-visible behavior: establish the necessary state, perform a small set of actions, and evaluate the result. Selenium describes browser tests as relatively expensive to run and says to use them where they provide value; a test that can be covered adequately at a lighter level may not need a browser.
When several tests need the same page locators or interactions, put that UI knowledge in a page object or component. Tests can then describe intent while the page object provides page-specific services. Selenium’s guidance generally keeps outcome assertions in test code, rather than making page objects responsible for deciding whether the test passed.
# Conceptual Python example; imports and selectors depend on your application.
class LoginPage:
def __init__(self, driver):
self.driver = driver
def open(self, base_url):
self.driver.get(f"{base_url}/login")
def sign_in(self, email, password):
self.driver.find_element("id", "email").send_keys(email)
self.driver.find_element("id", "password").send_keys(password)
self.driver.find_element("css selector", "button[type='submit']").click()
def test_member_can_sign_in(driver, base_url, member_credentials):
page = LoginPage(driver)
page.open(base_url)
page.sign_in(member_credentials["email"], member_credentials["password"])
# Keep the test outcome assertion in the test.
assert driver.current_url.endswith("/account")
The example illustrates the separation, not a complete fixture setup. Define the driver lifecycle, base URL, waits, and credentials using the project’s test framework. Do not copy selectors blindly; use selectors that match the application and are stable enough for its tests.
4. Choose a deliberate policy for test data and secrets
There is no single Selenium rule for storing fixtures, spreadsheets, or credentials. Decide what the team needs and make the policy explicit:
- Use deterministic, reviewable fixtures for data that is safe to keep with the tests. Record their format and how tests reset or consume them.
- Keep private credentials, tokens, and production data out of ordinary committed files. Supply sensitive values through the team’s approved local or CI secret mechanism.
- Commit a template such as
.env.examplewith variable names and harmless placeholders, not working secrets. - For a data file that changes with the tests, review its diffs and document who owns it and how to regenerate it. Binary spreadsheet changes can be difficult to inspect in a normal text diff, so consider whether a text fixture format better fits the use.
- Make each test’s data dependencies visible. Avoid hidden reliance on a developer’s local database state or a manually prepared account that the README never mentions.
These are team design recommendations, not Selenium-prescribed storage rules. For example, a small stable fixture may be appropriate to commit, while live customer records or credentials should not be placed in a shared test file.
5. Use Git changes that teammates can review
Git itself is not Selenium-specific. A straightforward contribution sequence is:
git status
git add tests pages README.md requirements.txt
git diff --cached
git commit -m "Add login behavior test"
Adjust the paths and commit message to the actual change. Before committing, inspect staged changes for accidental secrets, local output, unrelated edits, and missing dependency or documentation updates. Teams should document their chosen branch, review, and merge policies; there is no universal Selenium branching model in Selenium’s guidance.
A source-control setup is useful only if it lets another contributor reproduce the run. After cloning, follow the README from a clean environment and fix missing steps as they appear. Contributors should run the documented relevant tests before sharing changes, subject to the team’s own workflow.
6. Troubleshoot common source-control and run failures
| Symptom | Likely cause | What to check or fix |
|---|---|---|
| Dependencies cannot be installed after clone | The manifest is missing, stale, or the README names the wrong package manager or runtime. | Check in the project’s dependency and runner configuration. Update the documented install command and any lockfile or wrapper expected by the team. |
| Browser or driver cannot be found | Browser setup, network access, versions, or environment constraints differ from the developer machine. | Confirm the supported browser and Selenium binding version. Check whether Selenium Manager can run in the environment, and document any required proxy, pinned browser, or managed driver setup. |
| Tests pass locally but fail for a teammate | Undocumented local state, different versions, missing environment variables, or non-deterministic test data. | Reproduce from a clean clone, compare runtime and dependency setup, list required configuration, and make test data setup/reset explicit. |
| Secret appears in a proposed commit | A credential was added to source or staged from a local config file. | Remove it from the change and rotate it if it was exposed. Add the local secret file to the team’s ignore policy and use the approved secret delivery process. Removing it in a later commit does not erase it from earlier Git history. |
| Spreadsheet fixture changes are hard to review | The file is binary or its diff does not expose the data changes clearly. | Document ownership and update steps; consider a text-based fixture if practical. If a spreadsheet is necessary, make its expected schema and validation clear. |
| One test cannot be selected with the documented command | The selector syntax belongs to a different runner or is not configured in the project. | Check the actual runner’s selection syntax and put a working file-level or test-level example in the README. |
| Browser tests are slow or costly to run | The suite exercises too much through a real browser or repeats setup unnecessarily. | Keep browser tests focused on end-user flows that need a browser and cover suitable lower-level behavior with lighter tests. Selenium’s overview notes that functional end-user tests such as Selenium tests are expensive to run. |
7. Reliability, runtime, and maintenance
Source control improves repeatability when the repository records the inputs that matter: test code, dependencies, runner settings, setup instructions, safe fixtures, and clear configuration requirements. It cannot by itself make browser behavior deterministic. Keep tests focused, make their data dependencies explicit, and centralize repeated page mechanics where that improves maintainability.
Browser tests require browser execution and supporting infrastructure, so their runtime and maintenance costs depend on suite size, environment, and the behavior under test. No universal Selenium runtime or cost benchmark follows from the guidance here. Track the suite in the project’s own environment and choose the lightest test level that gives adequate confidence.
Or skip the browser setup
If you need screenshots of the pages your Selenium project visits, ScreenshotNeo provides a website screenshot API and MCP server. One GET request returns a PNG, JPEG, WebP, or PDF; see the API documentation for 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}`);
Cookie and consent banners, newsletter popups, and chat widgets are removed before the shot. Bot checks, blank pages, failed loads, timeouts, and cache hits are not billed; response headers report the page verdict and billing status. Its MCP server gives AI agents tools to take screenshots, get page information, and capture PDFs. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000 screenshots.
Sign up for 1,000 free screenshots a month, with no card required.
FAQ
Does Selenium require Git?
No. Git is a common way for teams to review and share test-project changes, but Selenium is a browser automation ecosystem and does not require a particular source-control system.
Should a page object contain assertions?
Selenium’s general guidance is to keep test outcome assertions in the test and use page objects to represent pages and the services they offer. Teams may shape their abstractions to their needs, but this is a useful default.
Is one directory structure best for every Selenium language?
No. Match the structure and commands to the chosen language, dependency manager, and test runner, then document them for contributors.


