Spring Boot
@Configuration
Testing
@Test
Java

Override a single Configuration class on every spring boot Test

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

If you need the same configuration override in every Spring Boot test, the scalable fix is to centralize the override once and make all tests inherit or import it consistently. Repeating @MockBean, @Import, or bean replacement logic in every class works for a while, but it becomes noisy and error-prone fast.

A Clean Pattern: Shared @TestConfiguration

Suppose production code has a configuration that creates a real mail client:

java
1@Configuration
2public class MailClientConfig {
3    @Bean
4    MailClient mailClient() {
5        return new SmtpMailClient("smtp.prod.example");
6    }
7}

In tests, you might want the same fake client everywhere. A shared @TestConfiguration is a good fit:

java
1import org.springframework.boot.test.context.TestConfiguration;
2import org.springframework.context.annotation.Bean;
3import org.springframework.context.annotation.Primary;
4
5@TestConfiguration
6public class GlobalTestOverrides {
7    @Bean
8    @Primary
9    MailClient mailClient() {
10        return new FakeMailClient();
11    }
12}

@Primary makes the test bean win when Spring resolves the dependency. That is often safer than depending on bean-name replacement rules.

Apply It to Every Test with a Meta-Annotation

Instead of repeating @SpringBootTest and @Import everywhere, wrap them in one custom annotation:

java
1import java.lang.annotation.ElementType;
2import java.lang.annotation.Retention;
3import java.lang.annotation.RetentionPolicy;
4import java.lang.annotation.Target;
5
6import org.springframework.boot.test.context.SpringBootTest;
7import org.springframework.context.annotation.Import;
8
9@Target(ElementType.TYPE)
10@Retention(RetentionPolicy.RUNTIME)
11@SpringBootTest
12@Import(GlobalTestOverrides.class)
13public @interface IntegrationTest {
14}

Now every integration test can use the same override just by annotating the class:

java
1@IntegrationTest
2class OrderServiceTest {
3    // tests here
4}

This is much easier to maintain than copying configuration annotations into dozens of test classes.

Another Good Option: a Test Profile

If the override should apply to a broad set of tests, a dedicated Spring profile is also a strong option. You create a test-only configuration class in src/test/java and activate the test profile in your test base annotation or base class.

That approach is especially nice when the override is not just one bean but a whole test environment, such as an in-memory mail client, stub clock, and local storage adapter.

Why Not Just Enable Bean Definition Overriding

Some developers try to replace production beans by enabling bean definition overriding globally. That can work, but it makes the test setup less explicit and can hide collisions you actually wanted Spring to warn you about.

Using @Primary, @TestConfiguration, or a clearly scoped test profile keeps the override intentional and visible.

Slice Tests Need Extra Care

One subtle issue is that not every Spring test loads the same application context. A @WebMvcTest, @DataJpaTest, and @SpringBootTest all create different slices.

If you want one override on every kind of test, think carefully about whether the bean even belongs in that slice. A global override that makes sense in full integration tests may be irrelevant or invalid in a narrow slice test.

Common Pitfalls

The biggest pitfall is defining the test override but never importing it into the test context. A @TestConfiguration class sitting in the codebase does nothing by itself.

Another common issue is forgetting @Primary when both the production and test beans are visible. Spring then fails because it sees multiple candidates of the same type.

Developers also assume one global strategy works for every test slice. In reality, @SpringBootTest and narrower slice annotations often need slightly different handling.

Summary

  • Put the shared override in a reusable @TestConfiguration class.
  • Use @Primary when you want the test bean to win over the production bean.
  • Apply the override consistently through a custom meta-annotation or shared base test setup.
  • Consider a dedicated test profile when the override is part of a larger test environment.
  • Be careful with slice tests, because not every bean belongs in every test context.

Course illustration
Course illustration

All Rights Reserved.