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
.envfiles - 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
.envfile - 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 foundFor 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=trueBaseTestEnvironment constructor parameters:
populateSystemProperties: Boolean (default:true) - Whether to populate system properties with variables from.envdotEnvFileDir: String (default:"./") - Directory containing the.envfiledotEnvFileName: String (default:".env") - Name of the environment file
The environment loading is designed to be fault-tolerant:
- Missing
.envfiles are ignored - Malformed
.envfiles are ignored - Missing variables return
nullor 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
.envfiles - Add
.envfiles to.gitignore - Provide
.env.examplefiles 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
.envfile location matchesdotEnvFileDiranddotEnvFileName - Verify file permissions
- Ensure the file is properly formatted
- System Properties Not Updating
- Verify
populateSystemPropertiesis set totrue - Check for conflicts with existing system properties
- Unexpected Default Values
- Remember the precedence order: System env >
.envfile > 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")