TestEnvironment
and BaseTestEnvironment
are utility classes designed to help manage environment variables in your test environment. These classes provide a flexible way to:
- Read system environment variables
- Load variables from
.env
files - Provide fallback default values
- Populate system properties
The simplest way to use the test environment is through the TestEnvironment
singleton object:
import me.kpavlov.finchly.TestEnvironment
class MyTest {
@Test
fun `test with environment variables`() {
// Read an environment variable
val homeDir = TestEnvironment["HOME"]
// Read with a default fallback value
val apiKey = TestEnvironment.get("API_KEY", defaultValue = "default-key")
}
}
The environment classes follow this precedence order when reading variables:
- System environment variables (
System.getenv()
) - Values from
.env
file - Default values (if provided)
- Define your TestEnvironment as singleton
// Different ways to read variables
val value1 = TestEnvironment["MY_VAR"] // Returns null if not found
val value2 = TestEnvironment.get("MY_VAR", "default") // Returns "default" if not found
For more control over the environment configuration, you can create an instance of BaseTestEnvironment
:
val testEnv = BaseTestEnvironment(
populateSystemProperties = true, // Whether to populate system properties
dotEnvFileDir = "./config", // Directory containing .env file
dotEnvFileName = "test.env" // Name of the .env file
)
class MyCustomTest {
@Test
fun `test with custom environment`() {
val value = testEnv["DATABASE_URL"]
}
}
The classes support loading variables from .env
files. Here's an example .env
file:
DATABASE_URL=jdbc:postgresql://localhost:5432/testdb
API_KEY=secret-test-key
CACHE_ENABLED=true
BaseTestEnvironment
constructor parameters:
populateSystemProperties
: Boolean (default:true
) - Whether to populate system properties with variables from.env
dotEnvFileDir
: String (default:"./"
) - Directory containing the.env
filedotEnvFileName
: String (default:".env"
) - Name of the environment file
The environment loading is designed to be fault-tolerant:
- Missing
.env
files are ignored - Malformed
.env
files are ignored - Missing variables return
null
or the specified default value
-
Test-Specific Environments
class MyTest { private val testEnv = BaseTestEnvironment( dotEnvFileName = "test.env", dotEnvFileDir = "src/test/resources" ) }
-
Using Default Values
// Always provide meaningful defaults for optional configuration val timeout = testEnv.get("TIMEOUT_SECONDS", "30")
-
Environment File Management
- Keep sensitive values in
.env
files - Add
.env
files to.gitignore
- Provide
.env.example
files with template values
- Testing Different Configurations
class ConfigurationTest { private val prodEnv = BaseTestEnvironment(dotEnvFileName = "prod.env") private val stagingEnv = BaseTestEnvironment(dotEnvFileName = "staging.env") @Test fun `test configuration loading`() { assertThat(prodEnv["API_URL"]).isNotEqualTo(stagingEnv["API_URL"]) } }
class DatabaseIntegrationTest {
private val testEnv = BaseTestEnvironment()
private val dbUrl = testEnv.get("DATABASE_URL")
?: throw IllegalStateException("DATABASE_URL must be set")
@Test
fun `test database connection`() {
// Use dbUrl for testing
}
}
class MockServiceTest {
private val testEnv = BaseTestEnvironment()
private val mockServiceUrl = testEnv.get("MOCK_SERVICE_URL", "http://localhost:8080")
@Test
fun `test external service integration`() {
// Use mockServiceUrl for testing
}
}
- Variables Not Loading
- Check the
.env
file location matchesdotEnvFileDir
anddotEnvFileName
- Verify file permissions
- Ensure the file is properly formatted
- System Properties Not Updating
- Verify
populateSystemProperties
is set totrue
- Check for conflicts with existing system properties
- Unexpected Default Values
- Remember the precedence order: System env >
.env
file > default value - Use logging or debugging to verify which source is being used
// Old approach
val apiKey = System.getenv("API_KEY") ?: "default-key"
// New approach using TestEnvironment
val apiKey = TestEnvironment.get("API_KEY", "default-key")
// Old approach
val props = Properties().apply {
load(FileInputStream("config.properties"))
}
// New approach using BaseTestEnvironment
val testEnv = BaseTestEnvironment(dotEnvFileName = "config.env")