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:
@TestConfigurationclass 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.IamRestAutoConfiguration4. 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.)
Which one do I reach for?
Section titled “Which one do I reach for?”| 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) |