简体   繁体   中英

How to inject Beans inside a test class properly with spring boot and annotations

I have a class I want to test if it uses the right Beans when a certain profile is active. Therefore I have written a test class, with the profile active, for the DomesticService (which in turn uses the GardeningService and CleaningService, all of it is autowired).

@Component
public class HumanDomesticService implements DomesticService {
    private CleaningService cleaningService;
    private GardeningService gardeningService;
    private Logger logger;

    HumanDomesticService() {
    }

    @Autowired
    public HumanDomesticService(CleaningService cleaningService, GardeningService gardeningService, Logger logger) {
        setCleaningService(cleaningService);
        setGardeningService(gardeningService);
        setLogger(logger);
    }

I made a test configuration class, which should scan the whole project for Beans, since the SpringBootApplication annotation includes the ComponentScan annotation.

@SpringBootApplication
public class ActiveProfileConfig {
}

Yet my test class can't seem to find the right Beans to complete the test.

org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'be.mycompany.springlessons.housekeeping.domestic.service.ActiveProfileSmallHouseTest': Unsatisfied dependency expressed through field 'service'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'be.mycompany.springlessons.housekeeping.domestic.service.DomesticService' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}

After that I tried to make Beans inside my configuration class, which makes my tests succeed, but then Maven complains about finding 2 Beans which could be injected there. Maven seems to be able to see all the beans through the ComponentScan.

Finally I ended importing the necessary classes, which works for both my tests and Maven, but it just doesn't seem to be the right and best solution.

@SpringBootTest(classes = ActiveProfileConfig.class)
@ActiveProfiles(profiles = "smallHouse")
@Import({HumanDomesticService.class, HumanCleaningService.class, RobotCleaningService.class, HumanGardeningService.class, HedgeTrimmerFactory.class, Broom.class, VacuumCleaner.class,
        Sponge.class, DisposableDuster.class, LawnMower.class, LoggerFactory.class})
public class ActiveProfileSmallHouseTest {
    @Autowired
    private DomesticService service;

I have tried to search on the internet for another solution and saw I wasn't the only one with the problem, but no other solution seemed yet to have worked.

What is the reason ComponentScan doesn't seem to work in a test class and how best to solve this?

I have had the exact same problem for a long time. I was able to solve this in Spring Boot by adding the main configuration I wanted to use. and all the needed dependencies my test had (All AutoWires) to the classes field of the @SpringBootTest annotation.

@SpringBootTest(classes = {ActiveProfile.class, HumanDomesticService.class, HumanCleaningService.class, RobotCleaningService.class, HumanGardeningService.class, HedgeTrimmerFactory.class, Broom.class, VacuumCleaner.class,
    Sponge.class, DisposableDuster.class, LawnMower.class, LoggerFactory.class})
@ActiveProfiles({"smallhouse"})
public class ActiveProfileSmallHouseTest {
    @Autowired
    private DomesticService service;
...
}

If you weren't using Spring Boot. You could solve this with the @ContextConfiguration annotation. Which works in the same way and is a part of the @SpringBootTest annotation as well.

This is also how it is being done in my main learning source: Pro Spring 5 - Iuliana Cosmina et al - Apress (page 631)

I still don't know why the ComponentScan or automatic loading of the context doesn't work. But at least I know that this way my tests will always run. Also, you are able to run only the classes you need to get this test succeeding, and don't need to fire up the entire context for the test.

I wanted to share our final solution on this.

We both use IntelliJ IDEA as IDE. In this IDE we didn't specify that the tests shouldn't use the module path. IntelliJ now has a setting that lets you enable/disable the usage of the module path for Unit tests.

In maven we already disabled this by configuring the main surefire plugin to not use the module path.

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-surefire-plugin</artifactId>
    <version>3.0.0-M3</version>
    <configuration>
        <useModulePath>false</useModulePath>
    </configuration>
</plugin>

With this 'new' feature in our IDEA, we also have to do disable this so our tests don't use the class path.

We figured it out because our Maven phase: mvn test Did run the tests.

Now all the tests run smoothly with just the @SpringBootTest annotation.

To set this in IntelliJ:

  • in the menu bar: Run > Edit Configurations
  • in the left menu open the sub menu of Templates > JUnit
  • uncheck the checkbox next to Use module path
  • click apply > OK

From now on, all the tests you will run will not use this module path. If you already have existing tests. Select them win the menu on the left, and uncheck the Use module path option for all of them.

显示使用模块路径选项

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM