Skip to content

Recipe: Testing Your App With IAM

Problem: the moment the starter is on the classpath, IAM’s SecurityFilterChain, JWT filter, and tenant resolution activate. That’s what you want in production — but a MockMvc slice that just wants to exercise your controller now gets a 401, and a full-context test needs JWT/OAuth2/admin secrets just to start.

First, the thing that surprises people: you never run — or disable — IAM’s own tests. Maven Central ships only the compiled main jars (plus sources/javadoc); the test source sets stay in the repo. Your ./gradlew test runs your tests. What follows is about controlling IAM’s runtime inside those tests.

1. Boot a full context with zero secrets — iam.dev-mode

Section titled “1. Boot a full context with zero secrets — iam.dev-mode”

A @SpringBootTest fails fast if IAM’s strict IamSecretValidator finds no JWT/OAuth2/admin secrets. Turn on dev mode and an EnvironmentPostProcessor mints ephemeral ones before the context refreshes, so the app boots with no configuration:

@SpringBootTest(properties = ["iam.dev-mode=true"])
@ActiveProfiles("test")
class SmokeTest {
@Test fun `context loads`() {}
}

iam.dev-mode is dev/test only — the production secret validator is unchanged. Never set it in a deployed profile.

2. Let your controller test through — back IAM off with your own chain

Section titled “2. Let your controller test through — back IAM off with your own chain”

IAM’s chain is @Bean("iamSecurityFilterChain") @ConditionalOnMissingBean(SecurityFilterChain::class). Define any SecurityFilterChain in the test context and IAM backs off entirely — no changes to your production wiring:

@TestConfiguration
class PermitAllSecurity {
@Bean
fun testChain(http: HttpSecurity): SecurityFilterChain =
http.csrf { it.disable() }
.authorizeHttpRequests { it.anyRequest().permitAll() }
.build()
}
@WebMvcTest(LoanController::class)
@Import(PermitAllSecurity::class)
class LoanControllerTest { /* requests now reach the controller, unauthenticated */ }

This is the same @ConditionalOnMissingBean seam production hosts use to bring their own chain — a test is just its most common use.

3. Drop IAM entirely for a test — exclude the auto-configuration

Section titled “3. Drop IAM entirely for a test — exclude the auto-configuration”

When a test has no business touching IAM at all, exclude its two @AutoConfiguration classes so none of its beans are contributed:

@SpringBootTest(
properties = [
"spring.autoconfigure.exclude=" +
"dev.mpofusindie.iam.starter.IamAutoConfiguration," +
"dev.mpofusindie.iam.starter.IamRestAutoConfiguration"
]
)
class NoIamTest { /* IAM is absent — no filter, no tenant resolution, no beans */ }

Same effect from application-test.yml:

spring:
autoconfigure:
exclude:
- dev.mpofusindie.iam.starter.IamAutoConfiguration
- dev.mpofusindie.iam.starter.IamRestAutoConfiguration

4. Narrow the surface instead of removing it — feature toggles

Section titled “4. Narrow the surface instead of removing it — feature toggles”

Keep IAM on but quiet the parts a test doesn’t need:

iam:
oauth2:
enabled: false # skip the OAuth2/OIDC beans (no provider config needed)
cache:
enabled: false # resolve permissions fresh every call — no cache to prime or evict

(iam.cache.store already defaults to in-memory Caffeine, so a plain test needs no Redis.)

You want to… Use
Boot the whole app in a test with no secrets iam.dev-mode=true (§1)
Test your controller without auth getting in the way your own permissive SecurityFilterChain (§2)
Run a test where IAM must not participate at all spring.autoconfigure.exclude (§3)
Keep IAM but skip OAuth2/caching iam.oauth2.enabled / iam.cache.enabled (§4)