Lesson 16 / 25

Pitfall: Beans Declared on the Main Class

Why the first test run failed.

Slices still load the main configuration class

Test slices use the @SpringBootApplication class as their configuration, so @Bean methods declared on it are loaded in every slice. In the first version of the demo, a CommandLineRunner @Bean that seeded data lived in the main class: the web slice failed to start (no repository bean exists in a web slice) and the repository test saw the three seeded products in addition to its own. Moving the seeder into its own @Component, which slices filter out, fixed both: all 4 tests passed. Keep the main class minimal.

The first test run, before the fix

I ran mvn test on the first version; these lines are copied from that run (trimmed).

mvn test

Output:

[ERROR]   ProductRepositoryTest.derivedQueryFiltersAndSorts:22
Expecting actual:
  ["Notebook", "Notebook", "Fountain pen", "Pen"]
to contain exactly (and in same order):
  ["Notebook", "Pen"]
[ERROR]   ProductControllerTest.returnsProduct » IllegalState Failed to load ApplicationContext ...
[ERROR] Tests run: 4, Failures: 1, Errors: 3, Skipped: 0

The fix: a separate seeding component

This file is from a demo "shop" service generated by start.spring.io for Spring Boot 4.1.1 and Java 21 (Temurin 21.0.12), built with Maven 3.9.16; the project compiled and its tests passed. The seeder is now a @Component implementing CommandLineRunner; slices exclude it.

package com.example.shop;

import java.math.BigDecimal;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;

@Component   // a separate component, so test slices such as @WebMvcTest do not load it
public class DataSeeder implements CommandLineRunner {
    private final ProductRepository repo;

    public DataSeeder(ProductRepository repo) {
        this.repo = repo;
    }

    @Override
    public void run(String... args) {
        repo.save(new Product("Notebook", new BigDecimal("120.00")));
        repo.save(new Product("Fountain pen", new BigDecimal("899.00")));
        repo.save(new Product("Desk lamp", new BigDecimal("1499.00")));
    }
}

Quick check: Why did moving the seeder out of the main class fix the slice tests?

  • The main class cannot have methods
  • Components run faster
  • Slices load @Bean methods on the main class but filter out ordinary @Component classes
  • CommandLineRunner is deprecated
Answer

Slices load @Bean methods on the main class but filter out ordinary @Component classes — Keep the @SpringBootApplication class minimal.