Spring Boot 2单元测试:@Autowired换@MockBean生效的原因咨询
@Autowired Fails But @MockBean Works in Your Spring Boot Test Let's break down exactly what's happening here and why swapping @Autowired for @MockBean fixes your test error.
The Problem with @Autowired in Your Test
When you use @Autowired to inject VehicleRepository, Spring tries to find a real, fully initialized instance of that repository in its application context. But in your test setup:
- You're only using
@RunWith(SpringJUnit4ClassRunner.class)—this starts a basic Spring context, but it doesn't load your full Spring Boot application configuration (like scanning for@Repositorybeans or setting up database connections). - Even if you had a full context, unit testing a service shouldn't depend on a real repository (you don't want to hit a real database or test someone else's code).
Without a real VehicleRepository bean registered in the context, Spring throws that NoSuchBeanDefinitionException—it literally can't find the bean you're asking it to inject.
Why @MockBean Fixes Everything
@MockBean is a Spring Boot Test-specific annotation that solves this problem in two key ways:
- It creates a mock version of
VehicleRepositoryusing the Mockito library (no real database calls, no actual repository logic). - It automatically registers this mock bean into the Spring test context. Now when your test runs, Spring sees a bean of type
VehicleRepositoryexists, so it can inject it without errors.
This lets you isolate your service logic: you can define exactly how the mock repository should behave (e.g., return a specific vehicle when findById is called) and test only the code in VehicleServiceImpl.
A Better Test Setup Example
You can simplify your test even more by using @InjectMocks to let Mockito automatically wire the mock repository into your service, instead of manually creating the service instance:
@RunWith(SpringJUnit4ClassRunner.class) public class VehicleServiceImplTest { // Create and register a mock VehicleRepository in the Spring context @MockBean private VehicleRepository repository; // Automatically inject the mock repository into the service @InjectMocks private VehicleServiceImpl service; @Test public void testGetVehicleById() { // Define mock behavior Vehicle testVehicle = new Vehicle(); testVehicle.setId(1L); when(repository.findById(1L)).thenReturn(Optional.of(testVehicle)); // Test the service method Vehicle result = service.getVehicleById(1L); // Verify the result assertNotNull(result); assertEquals(1L, result.getId()); } }
If you don't even need the Spring context (for a pure unit test), you can skip Spring's runner entirely and use Mockito's own runner:
@RunWith(MockitoJUnitRunner.class) public class VehicleServiceImplTest { @Mock private VehicleRepository repository; @InjectMocks private VehicleServiceImpl service; // Same test method as above }
Key Takeaways
@Autowiredis for injecting real Spring-managed beans—use it when you need the actual implementation (e.g., in integration tests).@MockBeanis for creating and injecting mock beans into a Spring test context—perfect for unit testing services while isolating external dependencies like repositories.- For pure unit tests (no Spring context needed), use
@Mock+@InjectMockswithMockitoJUnitRunner.classfor a lighter, faster test setup.
内容的提问来源于stack exchange,提问作者robert trudel

