unit-test-config-properties
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.
What this skill does
# Unit Testing Configuration Properties and Profiles
## Overview
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:**
- Property prefix matches between `@ConfigurationProperties` and test properties
- Validation triggers on `@Validated` classes with invalid values
- Type conversions work for Duration, DataSize, collections, and maps
## When to Use
- Testing `@ConfigurationProperties` property binding
- Testing property name mapping and type conversions
- Validating configuration with `@NotBlank`, `@Min`, `@Max`, `@Email` constraints
- Testing environment-specific configurations (dev, prod)
- Testing nested property structures and collections
- Verifying default values when properties are not specified
- Fast configuration tests without Spring context startup
## Instructions
1. **Set up test dependencies**: Add `spring-boot-starter-test` and AssertJ dependencies
2. **Use ApplicationContextRunner**: Test property bindings without starting full Spring context
3. **Define property prefixes**: Ensure `@ConfigurationProperties(prefix = "...")` matches test property paths
4. **Test all property paths**: Verify each property including nested structures and collections
5. **Test validation constraints**: Use `context.hasFailed()` to verify `@Validated` properties reject invalid values
6. **Test type conversions**: Verify Duration (`30s`), DataSize (`50MB`), collections, and maps convert correctly
7. **Test default values**: Verify properties have correct defaults when not specified in test properties
8. **Test profile-specific configs**: Use `@Profile` with `ApplicationContextRunner` for environment-specific configurations
9. **Test edge cases**: Include empty strings, null values, and type mismatches
**Troubleshooting flow:**
- If properties don't bind → Check prefix matches (kebab-case to camelCase conversion)
- If validation doesn't trigger → Verify `@Validated` annotation is present
- If context fails to start → Check dependencies and `@ConfigurationProperties` class structure
## Examples
### Setup: Test Dependencies
```xml
<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>
```
### Basic Pattern: Property Binding
```java
@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();
});
}
}
```
### Validation Testing
```java
@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");
});
}
}
```
### Type Conversion Testing
```java
@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`.
## Best Practices
- **Test all property bindings** including nested structures and collections
- **Test validation constraints** for all `@NotBlank`, `@Min`, `@Max`, `@Email`, `@Positive` annotations
- **Test both default and custom values** to verify fallback behavior
- **Use ApplicationContextRunner** for fast context-free testing
- **Test profile-specific configurations** separately with `@Profile`
- **Verify type conversions** for Duration, DataSize, collections, and maps
- **Test edge cases**: empty strings, null values, type mismatches, out-of-range values
## Constraints and Warnings
- **Kebab-case to camelCase**: Property `app.my-property` maps to `myProperty` in Java
- **Loose binding**: Spring Boot uses loose binding by default; use strict binding if needed
- **`@Validated` required**: Add `@Validated` annotation to enable constraint validation
- **`@ConstructorBinding`**: All parameters must be bindable when using constructor binding
- **List indexing**: Use `[0]`, `[1]` notation; ensure sequential indexing for lists
- **Duration format**: Accepts ISO-8601 (`PT30S`) or simple syntax (`30s`, `1m`, `2h`)
- **Context isolation**: Each `ApplicationContextRunner` creates a new context with no shared state
- **Profile activation**: Use `spring.profiles.active=profileName` inRelated in Code Review
gstack
IncludedFast headless browser for QA testing and site dogfooding. Navigate pages, interact with elements, verify state, diff before/after, take annotated screenshots, test responsive layouts, forms, uploads, dialogs, and capture bug evidence. Use when asked to open or test a site, verify a deployment, dogfood a user flow, or file a bug with screenshots. (gstack)
startup-due-diligence
IncludedLegal due diligence review for seed-stage and Series A startups (US, Delaware C-Corp focus). Supports both investor and founder perspectives. Capabilities include: (1) Interactive document review and issue spotting; (2) Document request list generation; (3) Cap table and SAFE/convertible note analysis; (4) Red flag identification with severity ratings; (5) Diligence report generation. TRIGGERS: due diligence, DD, startup investment, cap table review, Series A, seed round, investor diligence, legal review startup, SAFE analysis, convertible note, 409A, founder vesting.
interview-master
IncludedThis skill should be used when the user asks to "generate interview questions", "prepare for interview", "optimize resume", "conduct mock interview", "analyze git commits for resume", "generate resume from code", "review my resume", or mentions interview preparation, career assistance, or extracting project experience from git history. Provides comprehensive interview and career development guidance for both job seekers and interviewers.
fix-issue
IncludedFixes GitHub issues using parallel analysis agents for root cause investigation, code exploration, and regression detection. Reads issue context from gh CLI, searches codebase and memory for related patterns, generates a fix with tests, and links the resolution back to the issue via PR. Includes prevention analysis to avoid recurrence. Use when debugging errors, resolving regressions, fixing bugs, or triaging issues.
sf-apex
IncludedGenerates and reviews Salesforce Apex code with 150-point scoring. TRIGGER when: user writes, reviews, or fixes Apex classes, triggers, test classes, batch/queueable/schedulable jobs, or touches .cls/.trigger files. DO NOT TRIGGER when: LWC JavaScript (use sf-lwc), Flow XML (use sf-flow), SOQL-only queries (use sf-soql), or non-Salesforce code.
swift-development
IncludedComprehensive Swift development for building, testing, and deploying iOS/macOS applications. Use when Claude needs to: (1) Build Swift packages or Xcode projects from command line, (2) Run tests with XCTest or Swift Testing framework, (3) Manage iOS simulators with simctl, (4) Handle code signing, provisioning profiles, and app distribution, (5) Format or lint Swift code with SwiftFormat/SwiftLint, (6) Work with Swift Package Manager (SPM), (7) Implement Swift 6 concurrency patterns (async/await, actors, Sendable), (8) Create SwiftUI views with MVVM architecture, (9) Set up Core Data or SwiftData persistence, or any other Swift/iOS/macOS development tasks.