Provides patterns for unit testing `@ConfigurationProperties` classes with `@ConfigurationPropertiesTest`. Validates property binding, tests validation constraints, verifies default values, checks type conversions, and mocks property sources for Spring Boot configuration properties. Use when testing application configuration binding, validating YAML or application.properties files, verifying environment-specific settings, or testing nested property structures.
npx skills add https://github.com/giuseppe-trisciuoglio/developer-kit --skill unit-test-config-properties
This skill provides patterns for unit testing @ConfigurationProperties bindings, environment-specific configurations, and property validation using JUnit 5. Covers testing property name mapping, type conversions, validation constraints, nested structures, and profile-specific configurations without full Spring context startup.
Key validation checkpoints:
@ConfigurationProperties and test properties@Validated classes with invalid values@ConfigurationProperties property binding@NotBlank, @Min, @Max, @Email constraintsspring-boot-starter-test and AssertJ dependencies@ConfigurationProperties(prefix = "...") matches test property pathscontext.hasFailed() to verify @Validated properties reject invalid values30s), DataSize (50MB), collections, and maps convert correctly@Profile with ApplicationContextRunner for environment-specific configurationsTroubleshooting flow:
@Validated annotation is present@ConfigurationProperties class structure<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<scope>test</scope>
</dependency>
@ConfigurationProperties(prefix = "app.security")
@Data
public class SecurityProperties {
private String jwtSecret;
private long jwtExpirationMs;
private int maxLoginAttempts;
private boolean enableTwoFactor;
}
class SecurityPropertiesTest {
@Test
void shouldBindPropertiesFromEnvironment() {
new ApplicationContextRunner()
.withPropertyValues(
"app.security.jwtSecret=my-secret-key",
"app.security.jwtExpirationMs=3600000",
"app.security.maxLoginAttempts=5",
"app.security.enableTwoFactor=true"
)
.withBean(SecurityProperties.class)
.run(context -> {
SecurityProperties props = context.getBean(SecurityProperties.class);
assertThat(props.getJwtSecret()).isEqualTo("my-secret-key");
assertThat(props.getJwtExpirationMs()).isEqualTo(3600000L);
assertThat(props.getMaxLoginAttempts()).isEqualTo(5);
assertThat(props.isEnableTwoFactor()).isTrue();
});
}
@Test
void shouldUseDefaultValuesWhenPropertiesNotProvided() {
new ApplicationContextRunner()
.withPropertyValues("app.security.jwtSecret=key")
.withBean(SecurityProperties.class)
.run(context -> {
SecurityProperties props = context.getBean(SecurityProperties.class);
assertThat(props.getJwtSecret()).isEqualTo("key");
assertThat(props.getMaxLoginAttempts()).isZero();
});
}
}
@ConfigurationProperties(prefix = "app.server")
@Data
@Validated
public class ServerProperties {
@NotBlank
private String host;
@Min(1)
@Max(65535)
private int port = 8080;
@Positive
private int threadPoolSize;
}
class ConfigurationValidationTest {
@Test
void shouldFailValidationWhenHostIsBlank() {
new ApplicationContextRunner()
.withPropertyValues(
"app.server.host=",
"app.server.port=8080",
"app.server.threadPoolSize=10"
)
.withBean(ServerProperties.class)
.run(context -> {
assertThat(context).hasFailed()
.getFailure()
.hasMessageContaining("host");
});
}
@Test
void shouldPassValidationWithValidConfiguration() {
new ApplicationContextRunner()
.withPropertyValues(
"app.server.host=localhost",
"app.server.port=8080",
"app.server.threadPoolSize=10"
)
.withBean(ServerProperties.class)
.run(context -> {
assertThat(context).hasNotFailed();
assertThat(context.getBean(ServerProperties.class).getHost()).isEqualTo("localhost");
});
}
}
@ConfigurationProperties(prefix = "app.features")
@Data
public class FeatureProperties {
private Duration cacheExpiry = Duration.ofMinutes(10);
private DataSize maxUploadSize = DataSize.ofMegabytes(100);
private List<String> enabledFeatures;
private Map<String, String> featureFlags;
}
class TypeConversionTest {
@Test
void shouldConvertDurationFromString() {
new ApplicationContextRunner()
.withPropertyValues("app.features.cacheExpiry=30s")
.withBean(FeatureProperties.class)
.run(context -> {
assertThat(context.getBean(FeatureProperties.class).getCacheExpiry())
.isEqualTo(Duration.ofSeconds(30));
});
}
@Test
void shouldConvertCommaDelimitedList() {
new ApplicationContextRunner()
.withPropertyValues("app.features.enabledFeatures=feature1,feature2")
.withBean(FeatureProperties.class)
.run(context -> {
assertThat(context.getBean(FeatureProperties.class).getEnabledFeatures())
.containsExactly("feature1", "feature2");
});
}
}
For nested properties, profile-specific configurations, collection binding, and advanced validation patterns, see references/advanced-examples.md.
@NotBlank, @Min, @Max, @Email, @Positive annotations@Profileapp.my-property maps to myProperty in Java@Validated required: Add @Validated annotation to enable constraint validation@ConstructorBinding: All parameters must be bindable when using constructor binding[0], [1] notation; ensure sequential indexing for listsPT30S) or simple syntax (30s, 1m, 2h)ApplicationContextRunner creates a new context with no shared statespring.profiles.active=profileName in withPropertyValues() for profile tests| Issue | Cause | Solution |
|-------|-------|----------|
| Properties not binding | Prefix mismatch | Verify @ConfigurationProperties(prefix="...") matches property paths |
| Validation not triggered | Missing @Validated | Add @Validated annotation to configuration class |
| Context fails to start | Missing dependencies | Ensure spring-boot-starter-test is in test scope |
| Nested properties null | Inner class missing | Use @Data on nested classes or provide getters/setters |
| Collection binding fails | Wrong indexing | Use [0], [1] notation, not (0), (1) |
Toolkit for interacting with and testing local web applications using Playwright. Supports verifying frontend functionality, debugging UI behavior, capturing browser screenshots, and viewing browser logs.
Use when implementation is complete, all tests pass, and you need to decide how to integrate the work - guides completion of development work by presenting structured options for merge, PR, or cleanup
Use when implementing any feature or bugfix, before writing implementation code
Use when encountering any bug, test failure, or unexpected behavior, before proposing fixes
Use when about to claim work is complete, fixed, or passing, before committing or creating PRs - requires running verification commands and confirming output before making any success claims; evidence before assertions always
Expert guidance for systematic backtesting of trading strategies. Use when developing, testing, stress-testing, or validating quantitative trading strategies. Covers "beating ideas to death" methodology, parameter robustness testing, slippage modeling, bias prevention, and interpreting backtest results. Applicable when user asks about backtesting, strategy validation, robustness testing, avoiding overfitting, or systematic trading development.
Cloud laboratory platform for automated protein testing and validation. Use when designing proteins and needing experimental validation including binding assays, expression testing, thermostability measurements, enzyme activity assays, or protein sequence optimization. Also use for submitting experiments via API, tracking experiment status, downloading results, optimizing protein sequences for better expression using computational tools (NetSolP, SoluProt, SolubleMPNN, ESM), or managing protein design workflows with wet-lab validation.
This skill should be used for time series machine learning tasks including classification, regression, clustering, forecasting, anomaly detection, segmentation, and similarity search. Use when working with temporal data, sequential patterns, or time-indexed observations requiring specialized algorithms beyond standard ML approaches. Particularly suited for univariate and multivariate time series analysis with scikit-learn compatible APIs.
Take giuseppe-trisciuoglio/unit-test-config-properties from the repository into ~/.claude/skills for personal
use, or into .claude/skills inside a project.
The agent identifies a skill by the name field in its header. Two skills with the
same name cannot sit side by side — one of them will be ignored.