or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

index.mdjson-testing.mdtest-infrastructure.md
tile.json

test-infrastructure.mddocs/

Test Infrastructure

Guide for implementing custom test slice annotations and controlling test auto-configuration.

Overview

Foundational infrastructure for domain-specific test slices enabling focused integration testing by loading only relevant components.

Key components: TestSliceTestContextBootstrapper<T>, @OverrideAutoConfiguration, Context customizers, Type exclude filters

Quick Reference: Infrastructure Components

ComponentPurposeWhen to Use
TestSliceTestContextBootstrapper<T>Base bootstrapper for test slicesCreating custom test slice annotations
@OverrideAutoConfigurationControl auto-configuration loadingDisabling default auto-config for focused tests
ContextCustomizerFactoryCustomize test application contextAdvanced test context modification
TypeExcludeFilterFilter component scanningControl which beans are loaded in test slice
@AutoConfigureDataSourceInitializationInitialize DataSource with SQL scriptsDatabase setup for tests

TestSliceTestContextBootstrapper<T>

Base class for custom test slice TestContextBootstrapper instances.

API

package org.springframework.boot.test.autoconfigure;

import org.springframework.boot.test.context.SpringBootTestContextBootstrapper;
import org.jspecify.annotations.Nullable;
import java.lang.annotation.Annotation;

/**
 * Base class for test slice TestContextBootstrappers.
 *
 * @param <T> the test slice annotation type
 */
public abstract class TestSliceTestContextBootstrapper<T extends Annotation>
        extends SpringBootTestContextBootstrapper {

    /**
     * Create a new TestSliceTestContextBootstrapper instance.
     * The constructor resolves the generic type parameter T from the subclass
     * and stores it internally for property extraction from test annotations.
     */
    protected TestSliceTestContextBootstrapper() {
        // Internally resolves type parameter T using ResolvableType and stores annotation type
    }

    /**
     * Extract properties from the test slice annotation.
     * Uses MergedAnnotations API to search the class hierarchy (including enclosing classes)
     * and extract the "properties" attribute from annotation T.
     * Override to customize property extraction.
     *
     * @param testClass the test class
     * @return the properties array from annotation T, or null if annotation not present
     */
    @Override
    protected String @Nullable [] getProperties(Class<?> testClass) {
        // Searches class hierarchy for annotation T and extracts properties() attribute
    }
}

Type Parameter Resolution

Generic type resolution identifies test slice annotation type for automatic property extraction.

How it works: Extend with concrete type → Framework resolves T at runtime → Extracts metadata via reflection → getProperties() finds properties() attribute

Creating Custom Test Slices

Step 1: Test Annotation

package com.example.test;

import org.springframework.boot.test.autoconfigure.OverrideAutoConfiguration;
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
import org.springframework.test.context.BootstrapWith;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.context.annotation.FilterType;

import java.lang.annotation.*;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@BootstrapWith(MyFeatureTestContextBootstrapper.class)
@ExtendWith(SpringExtension.class)
@OverrideAutoConfiguration(enabled = false)
@TypeExcludeFilters(MyFeatureTypeExcludeFilter.class)
@ImportAutoConfiguration
public @interface MyFeatureTest {
    String[] properties() default {};
    boolean useDefaultFilters() default true;
    Filter[] includeFilters() default {};
    Filter[] excludeFilters() default {};
    Class<?>[] excludeAutoConfiguration() default {};
}

Step 2: Bootstrapper

package com.example.test;

import org.springframework.boot.test.autoconfigure.TestSliceTestContextBootstrapper;

public class MyFeatureTestContextBootstrapper
        extends TestSliceTestContextBootstrapper<MyFeatureTest> {}

Step 3: Type Exclude Filter

package com.example.test;

import org.springframework.boot.test.context.filter.annotation.StandardAnnotationCustomizableTypeExcludeFilter;
import org.springframework.context.annotation.ComponentScan;

/**
 * TypeExcludeFilter for @MyFeatureTest that limits scanning to
 * @MyFeatureComponent annotated beans.
 */
public class MyFeatureTypeExcludeFilter
        extends StandardAnnotationCustomizableTypeExcludeFilter<MyFeatureTest> {

    @Override
    protected boolean hasAnnotation() {
        return getAnnotation() != null;
    }

    @Override
    protected ComponentScan.Filter[] getFilters(FilterType type) {
        MyFeatureTest annotation = getAnnotation();
        if (annotation == null) {
            return new ComponentScan.Filter[0];
        }

        return switch (type) {
            case INCLUDE -> annotation.includeFilters();
            case EXCLUDE -> annotation.excludeFilters();
        };
    }

    @Override
    protected boolean isUseDefaultFilters() {
        MyFeatureTest annotation = getAnnotation();
        return annotation == null || annotation.useDefaultFilters();
    }

    @Override
    protected Set<Class<?>> getDefaultIncludes() {
        // Include @MyFeatureComponent by default
        return Set.of(MyFeatureComponent.class);
    }

    @Override
    protected Set<Class<?>> getComponentIncludes() {
        // Additional component types to include
        return Collections.emptySet();
    }
}

Step 4: Define Component Marker Annotation

package com.example.test;

import org.springframework.stereotype.Component;
import java.lang.annotation.*;

/**
 * Marker annotation for components that should be loaded in @MyFeatureTest.
 */
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface MyFeatureComponent {
}

Step 5: Create Auto-Configuration Imports File

Create file: src/test/resources/META-INF/spring/org.springframework.boot.test.autoconfigure.MyFeatureTest.imports

com.example.config.MyFeatureAutoConfiguration
com.example.config.MyFeatureDependencyAutoConfiguration
org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration

One fully-qualified class name per line.

Step 6: Use Test Slice

@MyFeatureTest
public class MyFeatureTests {
    @Autowired
    private MyFeatureService myFeatureService;

    @Test
    public void testFeature() {
        String result = myFeatureService.process("input");
        assertThat(result).isEqualTo("processed-input");
    }
}

Advanced Bootstrapper Customization

Property extraction:

public class CustomBootstrapper extends TestSliceTestContextBootstrapper<MyTest> {
    @Override
    protected String[] getProperties(Class<?> testClass) {
        MyTest annotation = testClass.getAnnotation(MyTest.class);
        if (annotation == null) return null;

        List<String> properties = new ArrayList<>(Arrays.asList(annotation.properties()));
        if (annotation.enableCache()) properties.add("spring.cache.type=simple");
        properties.add("spring.test.myfeature.enabled=true");
        return properties.toArray(new String[0]);
    }
}

Context initialization:

public class AdvancedBootstrapper extends TestSliceTestContextBootstrapper<MyTest> {
    @Override
    protected Set<Class<? extends ContextCustomizer>> getContextCustomizerFactoryClasses(
            MergedContextConfiguration config) {
        Set<Class<? extends ContextCustomizer>> factories = new LinkedHashSet<>(
                super.getContextCustomizerFactoryClasses(config));
        factories.add(MyCustomContextCustomizerFactory.class);
        return factories;
    }
}

@OverrideAutoConfiguration

Override @EnableAutoConfiguration behavior in tests.

API

package org.springframework.boot.test.autoconfigure;

/**
 * Annotation that can be used to override @EnableAutoConfiguration.
 * Often used with @ImportAutoConfiguration to precisely control
 * which auto-configurations are loaded in tests.
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface OverrideAutoConfiguration {

    /**
     * Value of the EnableAutoConfiguration enabled override property.
     * Set to false to disable auto-configuration, true to enable it.
     *
     * @return the override value
     */
    boolean enabled();
}

How It Works

When @OverrideAutoConfiguration(enabled = false) is present:

  1. Sets system property spring.boot.enableautoconfiguration=false
  2. Disables @EnableAutoConfiguration processing
  3. No auto-configurations loaded unless explicitly imported with @ImportAutoConfiguration

Usage Patterns

Pattern 1: Disable All Auto-Configuration

import org.springframework.boot.test.autoconfigure.OverrideAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
@OverrideAutoConfiguration(enabled = false)
public class NoAutoConfigTest {
    // No auto-configurations loaded
    // Manually configure all needed beans
}

Pattern 2: Disable Then Selectively Import

import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration;
import org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration;

@SpringBootTest
@OverrideAutoConfiguration(enabled = false)
@ImportAutoConfiguration({
    JacksonAutoConfiguration.class,
    WebMvcAutoConfiguration.class
})
public class SelectiveAutoConfigTest {
    // Only Jackson and WebMvc auto-configurations loaded
    // Much faster startup than full @SpringBootTest
}

Pattern 3: Use in Test Slice Annotations

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@BootstrapWith(MyTestContextBootstrapper.class)
@OverrideAutoConfiguration(enabled = false)  // Disable default auto-config
@ImportAutoConfiguration  // Import from META-INF/spring/*.imports
public @interface MyTest {
}

Pattern 4: Temporarily Enable for Debugging

@MyTest  // Has @OverrideAutoConfiguration(enabled = false) meta-annotation
@OverrideAutoConfiguration(enabled = true)  // Override the override to enable all auto-config
public class DebugTest {
    // All auto-configurations loaded for debugging
}

Import Strategies

Strategy 1: Explicit Class Import

@ImportAutoConfiguration({
    DataSourceAutoConfiguration.class,
    JdbcTemplateAutoConfiguration.class
})

Strategy 2: Imports File

Create META-INF/spring/org.springframework.boot.test.autoconfigure.MyTest.imports:

org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration
org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration

Then use:

@ImportAutoConfiguration  // Loads from imports file

Strategy 3: Auto-Configuration Classes Attribute

@ImportAutoConfiguration(classes = {
    DataSourceAutoConfiguration.class,
    JdbcTemplateAutoConfiguration.class
})

@AutoConfigureDataSourceInitialization

Enable DataSource initialization with SQL scripts in tests.

Complete API

package org.springframework.boot.test.autoconfigure.jdbc;

/**
 * Annotation that can be applied to test classes to enable DataSource
 * initialization. Imports DataSourceInitializationAutoConfiguration which
 * executes schema.sql and data.sql scripts from classpath.
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@ImportAutoConfiguration
public @interface AutoConfigureDataSourceInitialization {
}

What It Does

Imports DataSourceInitializationAutoConfiguration which:

  1. Searches for SQL scripts in classpath (default: src/test/resources/)
  2. Executes schema scripts to create database structure
  3. Executes data scripts to populate test data
  4. Supports platform-specific scripts
  5. Respects Spring Boot SQL initialization properties

Script Discovery

Default script locations:

  • schema.sql - Database schema creation
  • data.sql - Test data population

Platform-specific scripts (higher priority):

  • schema-${platform}.sql - e.g., schema-h2.sql, schema-postgresql.sql
  • data-${platform}.sql - e.g., data-h2.sql, data-postgresql.sql

Script search order:

  1. Platform-specific scripts (if spring.sql.init.platform set)
  2. Generic scripts (schema.sql, data.sql)

Usage Patterns

Pattern 1: Basic Usage with Default Scripts

@SpringBootTest
@AutoConfigureDataSourceInitialization
public class DatabaseTest {

    @Autowired
    private DataSource dataSource;

    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Test
    public void testDatabaseInitialized() {
        // schema.sql and data.sql executed before this test
        Integer count = jdbcTemplate.queryForObject(
            "SELECT COUNT(*) FROM users", Integer.class);

        assertThat(count).isGreaterThan(0);  // Data from data.sql
    }
}

Required files:

  • src/test/resources/schema.sql:

    CREATE TABLE users (
        id BIGINT PRIMARY KEY,
        username VARCHAR(255) NOT NULL,
        email VARCHAR(255)
    );
  • src/test/resources/data.sql:

    INSERT INTO users (id, username, email) VALUES (1, 'alice', 'alice@example.com');
    INSERT INTO users (id, username, email) VALUES (2, 'bob', 'bob@example.com');

Pattern 2: Custom Script Locations

@SpringBootTest(properties = {
    "spring.sql.init.schema-locations=classpath:sql/test-schema.sql",
    "spring.sql.init.data-locations=classpath:sql/test-data.sql"
})
@AutoConfigureDataSourceInitialization
public class CustomScriptLocationTest {
    // Uses sql/test-schema.sql and sql/test-data.sql
}

Pattern 3: Multiple Script Files

@SpringBootTest(properties = {
    "spring.sql.init.schema-locations=" +
        "classpath:schema/users.sql," +
        "classpath:schema/products.sql," +
        "classpath:schema/orders.sql",
    "spring.sql.init.data-locations=" +
        "classpath:data/users.sql," +
        "classpath:data/products.sql"
})
@AutoConfigureDataSourceInitialization
public class MultiScriptTest {
    // Executes multiple schema and data scripts in order
}

Pattern 4: Platform-Specific Scripts

@SpringBootTest(properties = {
    "spring.sql.init.platform=h2"
})
@AutoConfigureDataSourceInitialization
public class PlatformSpecificTest {
    // Uses schema-h2.sql and data-h2.sql instead of generic scripts
}

File structure:

src/test/resources/
  schema-h2.sql          # H2-specific schema
  schema-postgresql.sql  # PostgreSQL-specific schema
  data-h2.sql            # H2-specific data
  data-postgresql.sql    # PostgreSQL-specific data

Pattern 5: Control Initialization Mode

@SpringBootTest(properties = {
    "spring.sql.init.mode=always"  // always, embedded, never
})
@AutoConfigureDataSourceInitialization
public class InitModeTest {
    // Mode controls when scripts execute:
    // - always: Always execute scripts
    // - embedded: Only for embedded databases (default)
    // - never: Never execute scripts
}

Pattern 6: Error Handling

@SpringBootTest(properties = {
    "spring.sql.init.continue-on-error=false"  // Fail on SQL errors
})
@AutoConfigureDataSourceInitialization
public class StrictInitializationTest {
    // Test fails if any SQL script has errors
}

Integration with Test Slices

Many test slices automatically include @AutoConfigureDataSourceInitialization:

@DataJpaTest  // Includes @AutoConfigureDataSourceInitialization
public class JpaTest {
    // schema.sql and data.sql automatically executed
}

@JdbcTest  // Includes @AutoConfigureDataSourceInitialization
public class JdbcTest {
    // schema.sql and data.sql automatically executed
}

Configuration Properties

PropertyTypeDefaultDescription
spring.sql.init.modeenumembeddedWhen to execute scripts: always, embedded, never
spring.sql.init.schema-locationsString[]classpath:schema.sqlSchema script locations
spring.sql.init.data-locationsString[]classpath:data.sqlData script locations
spring.sql.init.platformStringallPlatform for SQL scripts
spring.sql.init.continue-on-errorbooleanfalseContinue on SQL errors
spring.sql.init.separatorString;SQL statement separator
spring.sql.init.encodingCharsetUTF-8SQL script encoding

Context Customizers

Advanced infrastructure for customizing test application contexts.

ContextCustomizerFactory

From: org.springframework.test.context (Spring Framework)

Create custom context customizers:

import org.springframework.test.context.ContextCustomizer;
import org.springframework.test.context.ContextCustomizerFactory;
import org.springframework.test.context.ContextConfigurationAttributes;
import org.springframework.context.ConfigurableApplicationContext;

public class MyContextCustomizerFactory implements ContextCustomizerFactory {

    @Override
    public ContextCustomizer createContextCustomizer(
            Class<?> testClass,
            List<ContextConfigurationAttributes> configAttributes) {

        // Check if customization needed
        MyTest annotation = testClass.getAnnotation(MyTest.class);
        if (annotation == null) {
            return null;
        }

        // Return customizer
        return new MyContextCustomizer(annotation);
    }

    private static class MyContextCustomizer implements ContextCustomizer {

        private final MyTest annotation;

        MyContextCustomizer(MyTest annotation) {
            this.annotation = annotation;
        }

        @Override
        public void customizeContext(
                ConfigurableApplicationContext context,
                MergedContextConfiguration mergedConfig) {

            // Customize the context
            ConfigurableEnvironment environment = context.getEnvironment();

            // Add properties
            Map<String, Object> properties = new HashMap<>();
            properties.put("my.feature.enabled", annotation.enableFeature());
            environment.getPropertySources().addFirst(
                new MapPropertySource("myTestProperties", properties));

            // Register beans
            context.getBeanFactory().registerSingleton(
                "myTestBean", new MyTestBean());
        }

        @Override
        public boolean equals(Object obj) {
            // Implement for context caching
            if (this == obj) return true;
            if (!(obj instanceof MyContextCustomizer other)) return false;
            return Objects.equals(this.annotation, other.annotation);
        }

        @Override
        public int hashCode() {
            // Implement for context caching
            return Objects.hash(annotation);
        }
    }
}

Register Context Customizer

Create META-INF/spring.factories:

org.springframework.test.context.ContextCustomizerFactory=\
com.example.test.MyContextCustomizerFactory

Built-in Context Customizers

This module provides internal context customizers:

  1. OnFailureConditionReportContextCustomizerFactory (internal)

    • Prints condition evaluation reports when context fails to start
    • Controlled by spring.test.print-condition-evaluation-report
  2. OverrideAutoConfigurationContextCustomizerFactory (internal)

    • Supports @OverrideAutoConfiguration annotation
    • Sets spring.boot.enableautoconfiguration property

Type Exclude Filters

Control component scanning in test slices.

StandardAnnotationCustomizableTypeExcludeFilter

From: org.springframework.boot.test.context.filter.annotation (spring-boot-test)

Base class for test slice type filters:

import org.springframework.boot.test.context.filter.annotation.StandardAnnotationCustomizableTypeExcludeFilter;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;

public class MyTypeExcludeFilter
        extends StandardAnnotationCustomizableTypeExcludeFilter<MyTest> {

    @Override
    protected boolean hasAnnotation() {
        // Check if test class has @MyTest
        return getAnnotation() != null;
    }

    @Override
    protected ComponentScan.Filter[] getFilters(FilterType type) {
        MyTest annotation = getAnnotation();
        if (annotation == null) {
            return new ComponentScan.Filter[0];
        }

        // Return filters from annotation
        return switch (type) {
            case INCLUDE -> annotation.includeFilters();
            case EXCLUDE -> annotation.excludeFilters();
        };
    }

    @Override
    protected boolean isUseDefaultFilters() {
        MyTest annotation = getAnnotation();
        return annotation == null || annotation.useDefaultFilters();
    }

    @Override
    protected Set<Class<?>> getDefaultIncludes() {
        // Component types to include by default
        return Set.of(MyFeatureComponent.class);
    }

    @Override
    protected Set<Class<?>> getComponentIncludes() {
        // Additional component types to include
        return Set.of(MyConfiguration.class, MyModule.class);
    }

    @Override
    public boolean match(MetadataReader metadataReader,
                        MetadataReaderFactory metadataReaderFactory) {
        // Custom matching logic if needed
        return super.match(metadataReader, metadataReaderFactory);
    }
}

Use Type Filter in Test Annotation

@TypeExcludeFilters(MyTypeExcludeFilter.class)
public @interface MyTest {
    // Filter applied to component scanning
}

Configuration Properties

spring.test.print-condition-evaluation-report

Type: boolean Default: true

Control condition evaluation report printing on test context failures.

spring:
  test:
    print-condition-evaluation-report: true

When true and ApplicationContext fails to start, prints detailed report:

============================
CONDITIONS EVALUATION REPORT
============================

Positive matches:
-----------------

   JacksonAutoConfiguration matched:
      - @ConditionalOnClass found required class 'com.fasterxml.jackson.databind.ObjectMapper'
      - @ConditionalOnMissingBean (types: com.fasterxml.jackson.databind.ObjectMapper; SearchStrategy: all) did not find any beans

Negative matches:
-----------------

   DataSourceAutoConfiguration:
      Did not match:
         - @ConditionalOnClass did not find required class 'javax.sql.DataSource' (OnClassCondition)

Exclusions:
-----------

   None

Unconditional classes:
----------------------

   org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration

Advanced Patterns

Pattern: Test Slice with Custom Properties

Create test slice with property mapping:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@BootstrapWith(CachingTestContextBootstrapper.class)
@OverrideAutoConfiguration(enabled = false)
@AutoConfigureCaching  // Custom annotation
@ImportAutoConfiguration
public @interface CachingTest {
    String[] properties() default {};
}

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@PropertyMapping("spring.test.caching")
public @interface AutoConfigureCaching {
    boolean enabled() default true;
    String cacheType() default "simple";
}

Use with property mapping:

@CachingTest
@AutoConfigureCaching(enabled = true, cacheType = "caffeine")
public class CacheTests {
    // Properties mapped to:
    // spring.test.caching.enabled=true
    // spring.test.caching.cache-type=caffeine
}

Pattern: Test Slice with Conditional Beans

Create beans conditionally based on test annotation:

@Configuration
@ConditionalOnMyTest
public class MyTestAutoConfiguration {

    @Bean
    @ConditionalOnMissingBean
    public MyTestService myTestService() {
        return new MyTestService();
    }

    @Bean
    public MyTestListener myTestListener() {
        return new MyTestListener();
    }
}

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Conditional(OnMyTestCondition.class)
public @interface ConditionalOnMyTest {
}

class OnMyTestCondition implements Condition {
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        // Check if running in @MyTest context
        return context.getBeanFactory() != null
            && context.getBeanFactory().containsBean("myTestMarkerBean");
    }
}

Pattern: Test Slice with Mock Beans

Automatically configure mock beans for test slice:

@MyFeatureTest
public class MyFeatureTestWithMocks {

    @Autowired
    private MyFeatureService myFeatureService;  // Real bean

    @MockBean  // From spring-boot-test
    private ExternalService externalService;  // Mocked dependency

    @Test
    public void testWithMock() {
        when(externalService.fetch()).thenReturn("mocked-data");

        String result = myFeatureService.process();

        assertThat(result).contains("mocked-data");
    }
}

Pattern: Nested Test Configuration

Use nested configuration classes in tests:

@MyFeatureTest
public class MyFeatureTests {

    @TestConfiguration
    static class TestConfig {

        @Bean
        @Primary
        public MyFeatureService testMyFeatureService() {
            return new MyFeatureService(customSettings());
        }

        private Settings customSettings() {
            return new Settings(/* test-specific settings */);
        }
    }

    @Autowired
    private MyFeatureService myFeatureService;  // Uses bean from TestConfig

    @Test
    public void testWithCustomConfig() {
        // Test with customized service
    }
}

Troubleshooting

Test Context Fails to Load

Symptom: ApplicationContext initialization fails with auto-configuration errors

Solutions:

  1. Enable condition evaluation report:

    spring.test.print-condition-evaluation-report: true

    Review output to see which auto-configurations matched/didn't match

  2. Check imports file exists: META-INF/spring/org.springframework.boot.test.autoconfigure.YourTest.imports

  3. Verify auto-configuration classes are correct:

    # Imports file - must be fully qualified class names
    com.example.MyAutoConfiguration
    org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration
  4. Check for missing dependencies:

    • Auto-configurations may require specific libraries on classpath
    • Add missing dependencies or exclude those auto-configurations
  5. Exclude problematic auto-configurations:

    @MyTest(excludeAutoConfiguration = ProblematicAutoConfiguration.class)

Custom Test Slice Not Loading Expected Components

Symptom: Beans expected to be loaded are not found

Solutions:

  1. Verify TypeExcludeFilter configured correctly:

    @TypeExcludeFilters(MyTypeExcludeFilter.class)
    public @interface MyTest {}
  2. Check getDefaultIncludes() returns correct types:

    @Override
    protected Set<Class<?>> getDefaultIncludes() {
        return Set.of(MyComponent.class, MyService.class);
    }
  3. Verify component has expected annotation:

    @MyComponent  // Must have this annotation to be included
    public class MyService {}
  4. Check useDefaultFilters setting:

    @MyTest(useDefaultFilters = true)  // Ensure not set to false
  5. Use includeFilters to add components:

    @MyTest(includeFilters = @Filter(type = FilterType.ASSIGNABLE_TYPE, classes = MyService.class))

Properties Not Applied to Test

Symptom: Properties defined in annotation not effective in test

Solutions:

  1. Verify properties() attribute present:

    @MyTest(properties = "my.property=value")
  2. Check bootstrapper extracts properties:

    @Override
    protected String[] getProperties(Class<?> testClass) {
        MyTest annotation = testClass.getAnnotation(MyTest.class);
        return annotation != null ? annotation.properties() : null;
    }
  3. Use correct property format:

    @MyTest(properties = {
        "key=value",  // Correct
        "spring.test.enabled=true"  // Correct
        // NOT: "key:value" or other formats
    })
  4. Check for property conflicts:

    • Later property sources override earlier ones
    • @TestPropertySource has higher priority than annotation properties

SQL Scripts Not Executed

Symptom: Database not initialized with schema/data scripts

Solutions:

  1. Verify annotation present:

    @SpringBootTest
    @AutoConfigureDataSourceInitialization  // Required
  2. Check script locations:

    • Default: src/test/resources/schema.sql and data.sql
    • Verify files exist and are in correct location
  3. Check initialization mode:

    @SpringBootTest(properties = "spring.sql.init.mode=always")
    @AutoConfigureDataSourceInitialization
  4. Check for SQL errors:

    @SpringBootTest(properties = "spring.sql.init.continue-on-error=false")
    @AutoConfigureDataSourceInitialization
    // Will fail fast on SQL errors
  5. Verify DataSource configured:

    @Test
    public void checkDataSource(@Autowired DataSource dataSource) {
        assertThat(dataSource).isNotNull();
    }
  6. Check platform setting:

    @SpringBootTest(properties = "spring.sql.init.platform=h2")
    // Looks for schema-h2.sql instead of schema.sql

Context Caching Issues

Symptom: Tests slow because context recreated for each test class

Solutions:

  1. Implement equals/hashCode in ContextCustomizer:

    @Override
    public boolean equals(Object obj) {
        // Proper implementation enables context caching
    }
    
    @Override
    public int hashCode() {
        // Must match equals() implementation
    }
  2. Use consistent test configuration:

    • Avoid different properties across test classes
    • Use same annotation settings where possible
  3. Check @DirtiesContext usage:

    • Excessive use prevents context caching
    • Only use when truly necessary
  4. Enable context cache logging:

    logging.level.org.springframework.test.context.cache: DEBUG

Best Practices

1. Test Slice Design

Focus on single concern:

@JsonTest  // Good: Tests only JSON serialization
@MyServiceTest  // Good: Tests only service layer
@MyFullStackTest  // Bad: Too broad, defeats test slice purpose

Load minimal components:

@OverrideAutoConfiguration(enabled = false)  // Disable all
@ImportAutoConfiguration({  // Import only needed
    JacksonAutoConfiguration.class,
    MyFeatureAutoConfiguration.class
})

2. Component Scanning Control

Use specific include filters:

@Override
protected Set<Class<?>> getDefaultIncludes() {
    // Be specific about what to include
    return Set.of(
        MyService.class,
        MyRepository.class,
        MyConfiguration.class
    );
}

Avoid loading everything:

// Bad
@MyTest(useDefaultFilters = false)  // Loads everything

// Good
@MyTest  // Loads only specified component types

3. Property Management

Use property mapping for reusable settings:

@PropertyMapping("spring.test.myfeature")
public @interface AutoConfigureMyFeature {
    boolean enabled() default true;
    int timeout() default 5000;
}
// Maps to spring.test.myfeature.enabled and spring.test.myfeature.timeout

Provide sensible defaults:

@MyTest(properties = {
    "spring.test.myfeature.enabled=true",  // Good default
    "spring.test.myfeature.timeout=5000"   // Reasonable timeout
})

4. Documentation

Document what test slice loads:

/**
 * Test slice for MyFeature functionality.
 * <p>
 * Loads only:
 * <ul>
 * <li>@MyFeatureComponent annotated beans</li>
 * <li>MyFeatureAutoConfiguration</li>
 * <li>Jackson auto-configuration (for JSON)</li>
 * </ul>
 * <p>
 * Does NOT load:
 * <ul>
 * <li>Web layer (controllers, filters)</li>
 * <li>Database layer (repositories, entities)</li>
 * <li>Security configuration</li>
 * </ul>
 */
@MyFeatureTest
public class MyTests {}

5. SQL Script Organization

Use clear naming:

src/test/resources/
  schema-h2.sql           # H2-specific schema
  schema-postgresql.sql   # PostgreSQL-specific schema
  data-test-users.sql     # Test user data
  data-test-products.sql  # Test product data

Keep scripts idempotent:

-- Good: Idempotent
DROP TABLE IF EXISTS users;
CREATE TABLE users (...);

-- Bad: Fails on second run
CREATE TABLE users (...);

6. Context Customizer Implementation

Implement equals/hashCode for caching:

private static class MyContextCustomizer implements ContextCustomizer {

    @Override
    public boolean equals(Object obj) {
        // Required for context caching
        if (this == obj) return true;
        if (!(obj instanceof MyContextCustomizer)) return false;
        MyContextCustomizer other = (MyContextCustomizer) obj;
        return Objects.equals(this.config, other.config);
    }

    @Override
    public int hashCode() {
        return Objects.hash(config);
    }
}

External Type Dependencies

From Spring Framework (org.springframework)

  • Annotation: java.lang.annotation.Annotation
  • TestContextBootstrapper: org.springframework.test.context.TestContextBootstrapper
  • ContextCustomizer: org.springframework.test.context.ContextCustomizer
  • ContextCustomizerFactory: org.springframework.test.context.ContextCustomizerFactory
  • ContextConfigurationAttributes: org.springframework.test.context.ContextConfigurationAttributes
  • MergedContextConfiguration: org.springframework.test.context.MergedContextConfiguration
  • ConfigurableApplicationContext: org.springframework.context.ConfigurableApplicationContext
  • ConfigurableEnvironment: org.springframework.core.env.ConfigurableEnvironment
  • ComponentScan: org.springframework.context.annotation.ComponentScan
  • Filter: org.springframework.context.annotation.ComponentScan.Filter
  • FilterType: org.springframework.context.annotation.FilterType

From spring-boot-test module

  • SpringBootTestContextBootstrapper: org.springframework.boot.test.context.SpringBootTestContextBootstrapper
  • StandardAnnotationCustomizableTypeExcludeFilter: org.springframework.boot.test.context.filter.annotation.StandardAnnotationCustomizableTypeExcludeFilter
  • @MockBean: org.springframework.boot.test.mock.mockito.MockBean
  • @SpyBean: org.springframework.boot.test.mock.mockito.SpyBean

From JUnit 5

  • @ExtendWith: org.junit.jupiter.api.extension.ExtendWith
  • SpringExtension: org.springframework.test.context.junit.jupiter.SpringExtension

Examples from Spring Boot

Spring Boot provides reference implementations of test slices using this infrastructure:

@JsonTest

Location: org.springframework.boot.test.autoconfigure.json Bootstrapper: JsonTestContextBootstrapper Includes: @JacksonComponent, JacksonModule beans Auto-configurations: Jackson, Gson, JSON-B

@WebMvcTest

Location: org.springframework.boot.test.autoconfigure.web.servlet (spring-boot-web-test) Bootstrapper: WebMvcTestContextBootstrapper Includes: @Controller, @ControllerAdvice, @JsonComponent Auto-configurations: Web MVC, Jackson, MessageConverters

@DataJpaTest

Location: org.springframework.boot.test.autoconfigure.orm.jpa (spring-boot-data-jpa-test) Bootstrapper: DataJpaTestContextBootstrapper Includes: @Entity, @Repository, @Embeddable Auto-configurations: JPA, DataSource, Hibernate, TestEntityManager

@RestClientTest

Location: org.springframework.boot.test.autoconfigure.web.client (spring-boot-restclient-test) Bootstrapper: RestClientTestContextBootstrapper Includes: REST client components Auto-configurations: REST client, Jackson, MockRestServiceServer

Each follows the patterns documented in this guide.