Spring Boot单元测试:ApplicationRunner Bean加载异常与@ConditionalOnProperty配置问题
Hey there! Let's tackle these two Spring Boot unit testing issues one by one with practical solutions:
1. How to Load Configuration with @ConditionalOnProperty in Unit Tests
Your CrawlerConfiguration combines both @ConditionalOnProperty and @Profile annotations, so we need to satisfy both conditions in your test setup. Here's how to make sure the configuration loads correctly:
- First, set the required property
batch=Crawlerto activate the configuration class. You can do this via@TestPropertySourceor directly in@SpringBootTest. - Second, activate the target profile (
testorproduction) using@ActiveProfilesto get the rightCrawlerbean.
Sample Test Class for CrawlerConfiguration
import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.ActiveProfiles; @SpringBootTest(properties = {"batch=Crawler"}) @ActiveProfiles("test") public class CrawlerConfigurationTest { @Autowired private Crawler crawler; @Test void testTestCrawlerBeanLoaded() { // Verify we're using the test-specific crawler (adjust based on your Crawler's methods) assert !crawler.isProductionMode(); assert crawler.getName().equals("TestCrawler"); } }
Lightweight Test (Without Full Context)
If you don't need the full Spring Boot context, use @SpringJUnitConfig to load only the necessary components:
import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.TestPropertySource; import org.springframework.context.annotation.Import; @SpringJUnitConfig @ActiveProfiles("test") @TestPropertySource(properties = {"batch=Crawler", "name=TestCrawler"}) @Import(CrawlerConfiguration.class) public class CrawlerConfigurationLightTest { @Autowired private Crawler crawler; @Test void testCrawlerInjection() { assert crawler != null; assert crawler.getName().equals("TestCrawler"); } }
2. Fixing Bean Definition Issues in ApplicationRunner Unit Tests
The main pain points here are either the runner executing unexpectedly during test setup, or the context failing to load the correct beans. Here are two reliable approaches:
Option 1: Test Runner Logic in Isolation
Extract the core logic of your ApplicationRunner into a separate component, then mock dependencies and test it directly (no full context needed):
import org.junit.jupiter.api.Test; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.boot.ApplicationArguments; @ExtendWith(MockitoExtension.class) public class MyApplicationRunnerTest { @Mock private ApplicationArguments args; @Mock private Crawler crawler; // Mock the dependency your runner uses @Test void testRunnerBusinessLogic() { MyApplicationRunner runner = new MyApplicationRunner(crawler); runner.run(args); // Verify interactions (adjust based on your runner's behavior) // Mockito.verify(crawler).startCrawling(); } }
Option 2: Load Full Context but Control Runner Execution
If you need the full context but don't want the runner to run automatically, use @MockBean to replace the actual runner:
import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.TestPropertySource; import org.springframework.beans.factory.annotation.Autowired; @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE) @ActiveProfiles("test") @TestPropertySource(properties = {"batch=Crawler", "name=TestCrawler"}) public class ApplicationRunnerContextTest { @MockBean private MyApplicationRunner applicationRunner; // Prevents actual runner from executing @Autowired private Crawler crawler; @Test void testCorrectBeanLoaded() { // Verify the crawler bean is properly injected assert crawler != null; assert !crawler.isProductionMode(); } }
If you want to test the runner's full behavior in context, remove the @MockBean and add assertions based on the runner's side effects (e.g., database updates, log entries).
内容的提问来源于stack exchange,提问作者Tsuyoshi

