Managing Environment Variables and Test Data in Test Automation: The Key to Reliable CI/CD Pipelines

Serhat Ozdursun
3 min readMar 4, 2025

--

One of the biggest challenges in test automation is managing environment-specific configurations. Hardcoding values such as:

  • Base URLs (dev, staging, prod)
  • User credentials (test accounts, API keys)
  • Test data (product IDs, feature flags, etc.)

leads to flaky tests, security risks, and unreliable test execution.

A structured approach to environment variables and test data ensures that tests run reliably across different environments without manual intervention.

Important: API keys, credentials, and other sensitive data should never be stored in configuration files or test scripts. Always use a secrets management system such as AWS Secrets Manager, Azure Key Vault, Jenkins Credentials Plugin, or GitHub Secrets to securely store and access them.

Best Practices for Managing Environment Variables

Use Configuration Files or Environment Variables

Instead of hardcoding values in test scripts, define dynamic environment variables that can be easily switched. Let’s explore different ways to implement this across JavaScript, Java, and Python.

Passing Environment Variables in Different Test Frameworks

JavaScript / WebDriverIO / Cypress

In WebDriverIO, you can pass environment variables using the CLI:

npx wdio run wdio.conf.js --env=staging

For Cypress:

cypress run --config env=staging

And access it inside the test script:

const baseUrl = Cypress.env('BASE_URL');
cy.visit(baseUrl);

If using a .env file in JavaScript projects, you can use dotenv:

require('dotenv').config();
const baseUrl = process.env.BASE_URL;

Java / Selenium with Maven

Use system properties to pass environment parameters dynamically:

mvn test -Denv=prod

Then, access it inside your Java test:

String env = System.getProperty("env", "staging"); // Default to staging
String baseUrl = env.equals("prod") ? "https://prod.example.com" : "https://staging.example.com";

Alternatively, you can use a config.properties file and load values dynamically:

Properties props = new Properties();
FileInputStream input = new FileInputStream("config.properties");
props.load(input);
String baseUrl = props.getProperty("BASE_URL");

Python / Pytest / Selenium

For Python-based automation, environment variables can be set using pytest arguments or .env files.

Run tests with:

pytest --env=prod

And access them in Python:

import os
import pytest

@pytest.fixture(scope="session")
def base_url():
env = os.getenv("ENV", "staging") # Default to staging
return "https://prod.example.com" if env == "prod" else "https://staging.example.com"
def test_example(base_url):
print(f"Running tests on {base_url}")

If using a .env file, install python-dotenv:

pip install python-dotenv

And load environment variables:

from dotenv import load_dotenv
import os

load_dotenv()
base_url = os.getenv("BASE_URL")

Integrating Environment Variables into CI/CD Pipelines

Modern CI/CD pipelines (e.g., GitHub Actions, GitLab CI, Jenkins) allow environment variables to be passed dynamically.

GitHub Actions Example

env:
ENV: prod
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Run tests
run: pytest --env=${{ env.ENV }}
secrets:
PROD_USER: ${{ secrets.PROD_USER }}
PROD_PASSWORD: ${{ secrets.PROD_PASSWORD }}

Jenkins Pipeline Example

pipeline {
agent any
environment {
ENV = 'staging'
STAGING_USER = credentials('staging-user')
STAGING_PASSWORD = credentials('staging-password')
}
stages {
stage('Run Tests') {
steps {
sh 'pytest --env=$ENV --user=$STAGING_USER --password=$STAGING_PASSWORD'
}
}
}
}

Managing Test Data with JSON for Different Environments

For test-specific data (such as product IDs or feature flags), store them in a JSON file separate from test scripts.

Best JSON Structure (testData.json)

{
"prod": {
"userName": "prodUser",
"password": "prodUserPassword",
"promotionProduct": "123545AD"
},
"staging": {
"userName": "stagingUser",
"password": "stagingUserPassword",
"promotionProduct": "98765BC"
}
}

Note: API keys, passwords, and other sensitive credentials should be stored in a secure vault, not in a JSON file or source code.

Using Secrets Management for Credentials

Use AWS Secrets Manager, Azure Key Vault, HashiCorp Vault, or Jenkins Credentials Plugin to securely store and retrieve credentials. Example for AWS Secrets Manager:

aws secretsmanager create-secret --name staging/secrets --secret-string '{ "userName": "stagingUser", "password": "stagingPassword" }'

And retrieve it in JavaScript:

const AWS = require('aws-sdk');
const secretsManager = new AWS.SecretsManager({ region: 'us-east-1' });
async function getSecret(secretName) {
const data = await secretsManager.getSecretValue({ SecretId: secretName }).promise();
return JSON.parse(data.SecretString);
}
(async () => {
const env = process.env.ENV || "staging";
const secrets = await getSecret(`${env}/secrets`);
console.log(`Running tests with ${secrets.userName} in ${env} environment`);
})();

Conclusion

A well-structured approach to environment variables and test data management ensures that your automated tests are:

  • Reliable — Tests run with the correct data for each environment.
  • Secure — Credentials and API keys are never exposed in source code.
  • Scalable — Works across multiple environments effortlessly.
  • CI/CD Ready — Easily integrates into automated pipelines.

To enhance security, always use secrets management tools like AWS Secrets Manager, Azure Key Vault, or Jenkins Credentials Plugin to handle sensitive data.

How do you manage environment variables and secrets in your test automation setup? Let’s discuss!

--

--

Serhat Ozdursun
Serhat Ozdursun

Written by Serhat Ozdursun

QA Automation Engineer at Index

No responses yet