Flattened bundle. Content from source markdown guides is inlined below.
How to test applications that use avaje-config.
Create test-specific configuration in src/test/resources:
src/test/resources/
├── application.yaml # Test defaults
├── application-test.yaml # Profile-specific
└── application-it.yaml # Integration test config
application-test.yaml:
server:
port: 0 # Use random port
database:
host: localhost
port: 5432
cache:
enabled: falseTests automatically use src/test/resources/application.yaml:
@Test
public void testConfiguration() {
String dbHost = Config.get("database.host");
assertEquals("localhost", dbHost);
}Override specific properties:
@Test
public void testWithCustomPort() {
System.setProperty("server.port", "9000");
try {
int port = Config.getInt("server.port");
assertEquals(9000, port);
} finally {
System.clearProperty("server.port");
}
}For advanced testing, mock the Config class:
import static org.mockito.Mockito.*;
@Test
public void testWithMockedConfig() {
// Create spy on real Config
Config spy = spy(Config.class);
when(spy.get("server.port")).thenReturn("9000");
int port = Integer.parseInt(spy.get("server.port"));
assertEquals(9000, port);
}Create a custom extension for configuration:
public class ConfigExtension implements BeforeEachCallback {
private Map<String, String> originalProperties;
@Override
public void beforeEach(ExtensionContext context) {
originalProperties = new HashMap<>();
// Save original values
originalProperties.put("server.port", System.getProperty("server.port"));
}
public void setProperty(String key, String value) {
System.setProperty(key, value);
}
public void reset() {
// Restore original values
originalProperties.forEach((key, value) -> {
if (value != null) {
System.setProperty(key, value);
} else {
System.clearProperty(key);
}
});
}
}Use in tests:
@ExtendWith(ConfigExtension.class)
public class MyTest {
@Test
public void test(ConfigExtension config) {
config.setProperty("server.port", "9000");
int port = Config.getInt("server.port");
assertEquals(9000, port);
}
}For integration tests with external services:
application-it.yaml:
server:
port: 8080
database:
host: localhost
port: 5432
name: test_db
redis:
host: localhost
port: 6379Use Docker Compose or Testcontainers:
public class IntegrationTest {
@ClassRule
public static DockerComposeContainer<?> environment =
new DockerComposeContainer<>(new File("docker-compose.it.yml"))
.withExposedService("postgres", 5432)
.withExposedService("redis", 6379);
@Test
public void testWithRealServices() {
String dbHost = Config.get("database.host");
// Test with real database and redis
}
}Test configuration change listeners:
@Test
public void testConfigChangeListener() {
List<String> changes = new ArrayList<>();
Config.addChangeListener(event -> {
changes.add(event.getProperty());
});
System.setProperty("server.port", "9000");
// Trigger configuration reload
Config.reload();
assertTrue(changes.contains("server.port"));
}| Practice | Reason |
|---|---|
| Use separate test config file | Prevents test pollution |
| Reset properties after tests | Clean state for next test |
| Use random ports | Allows parallel test execution |
| Mock external services | Faster, more reliable tests |
| Test both success and failure cases | Comprehensive coverage |
- Learn about environment variables in tests
- See troubleshooting for test issues
Common issues and solutions when using avaje-config.
Symptom: No property found for key: server.port
Solution:
- Check property name spelling and case sensitivity
- Ensure configuration file is in
src/main/resources/ - Verify the configuration file format is valid YAML
- Check for typos in environment variable names
Example:
# CORRECT
server:
port: 8080
# NOT server_port or serverPort in YAMLAccessing:
// Access nested properties with dot notation
int port = Config.getInt("server.port", 8080); // Provide defaultSymptom: application-prod.yaml is not being loaded
Solution:
- Verify profile is activated:
java -Dconfig.profile=prod myapp.jar - Check environment variable:
export CONFIG_PROFILE=prod - Ensure file name matches the profile:
application-PROFILE.yaml - Verify file is in
src/main/resources/
Check active profile:
String profile = Config.get("config.profile", "dev");
System.out.println("Active profile: " + profile);Symptom: ConfigChangeListener never triggers
Solution:
- Ensure listener is registered:
Config.addChangeListener(listener) - Verify configuration source supports change notifications
- Call
Config.reload()to trigger change detection - Check that the property actually changed
Debug:
Config.addChangeListener(event -> {
System.out.println("Config changed: " + event.getProperty());
});
// Force reload
Config.reload();Symptom: NumberFormatException or similar when getting property
Solution:
- Verify property value type matches the
getXxx()method - Provide a default value
- Check for leading/trailing whitespace in YAML
Examples:
# Correct types
server:
port: 8080 # Integer
ssl: true # Boolean
name: "localhost" # String// Matching types
int port = Config.getInt("server.port"); // OK
boolean ssl = Config.getBool("server.ssl"); // OK
String name = Config.get("server.name"); // OK
// Type mismatch - these will fail:
// int port = Config.getInt("server.name"); // NO - wrong type
// boolean ssl = Config.getBool("server.port"); // NO - wrong typeSymptom: Environment variables not replacing ${ENV_VAR} in YAML
Solution:
- Verify syntax:
${ENV_VAR:default}(colon before default) - Ensure environment variable is set:
export ENV_VAR=value - Check variable name matches convention (UPPERCASE_UNDERSCORE)
- Verify property is defined in YAML before setting
Example:
database:
host: ${DATABASE_HOST:localhost} # Correct: colon before default
port: ${DATABASE_PORT} # No default - env var required# Set the environment variable
export DATABASE_HOST=prod-db.example.com
export DATABASE_PORT=5432
java myapp.jarSymptom: No @Config class found for configuration
Solution:
- Ensure
@Configclass is on the classpath - Verify class is in scanned package
- Check for compilation errors
- Ensure dependency on
avaje-configis included
Example:
package com.example.config;
import io.avaje.config.Config;
@Config
public class DatabaseConfig {
public final String host;
public final int port;
public DatabaseConfig(String host, int port) {
this.host = host;
this.port = port;
}
}Symptom: Native image fails to start or missing configuration
Solution:
- Generate reflection metadata:
native-image -agentlib:native-image=config-output-dir=... - Ensure all config files are on classpath
- Use environment variables for dynamic values
- Verify GraalVM version compatibility
See Native Image Guide for detailed setup.
Symptom: Configuration loading is slow
Solution:
- Avoid loading configuration in tight loops
- Cache frequently accessed values
- Use
@Configclasses for groups of properties - Consider native image for faster startup
Optimization:
// Cache the value
private static final int CACHE_TTL = Config.getInt("cache.ttl", 300);
// Or use @Config class (loaded once)
@Config
public class CacheConfig {
public final int ttl;
public CacheConfig(int ttl) {
this.ttl = ttl;
}
}| Error | Cause | Solution |
|---|---|---|
NullPointerException |
Property not found | Provide default or check spelling |
NumberFormatException |
Wrong type conversion | Check property type matches method |
FileNotFoundException |
Config file missing | Move to src/main/resources/ |
ClassNotFoundException |
@Config class not scanned |
Check package location |
- Check the Avaje Config documentation
- Enable debug logging:
logging.level: DEBUG - Check the GitHub issues
- Join the Discord community