如何在单个实现类中注入两个Mockito模拟对象?当前仅一个模拟生效,BCryptPasswordEncoder注入出现空指针异常
Got it, let's break down what's going wrong here and fix your test setup step by step.
The Root Causes of Your Issues
- Mixed Annotation Usage: You're combining
@SpringBootTest(which spins up a full Spring context) with pure Mockito annotations like@Mockinstead of Spring Boot's@MockBean. This creates conflicts between Spring-managed beans and Mockito's mocks. - Manual Instance Creation: You're manually instantiating
UserServiceImplwhile using@InjectMocks—Mockito prefers to create the instance itself to handle dependency injection properly, so your manualnewbreaks this logic. - Unconfigured Mocks: For
BCryptPasswordEncoder, whether you use@Autowired(no Spring context in pure Mockito tests) or@Mock(no behavior set up), you end up with a null reference or an unconfigured mock that returnsnullwhen methods are called.
Solution 1: Pure Mockito Unit Test (Recommended for Fast Isolated Tests)
If you're writing a unit test for UserService (no need for a full Spring context), use Mockito's extension to handle mocks and injection:
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) public class UserServiceTests { // Mock both dependencies @Mock private BCryptPasswordEncoder passwordEncoder; @Mock private UserRepository userRepository; // Let Mockito create and inject dependencies into UserService @InjectMocks private UserServiceImpl userService; @Test void testUserCreation() { // Configure mock behaviors to avoid null pointers when(passwordEncoder.encode("raw-password")).thenReturn("encoded-password-123"); when(userRepository.save(any(User.class))).thenReturn(new User(1L, "test@example.com", "encoded-password-123")); // Call your service method User createdUser = userService.createUser("test@example.com", "raw-password"); // Add your assertions here assert createdUser.getId() == 1L; assert createdUser.getPassword().equals("encoded-password-123"); } }
Key Notes for This Setup:
- Remove
@SpringBootTestandMockitoAnnotations.initMocks(this)—@ExtendWith(MockitoExtension.class)handles mock initialization automatically. - Always configure mock behaviors with
when(...).thenReturn(...)(or similar) for any method calls your service makes. Without this, mocks return default values (likenull) which cause NPEs.
Solution 2: Spring Boot Integration Test (For Real Bean Dependencies)
If you need to use a real BCryptPasswordEncoder instance (e.g., to test actual password encoding logic), use @SpringBootTest with @MockBean to replace the UserRepository with a mock:
import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.mock.mockito.MockBean; @SpringBootTest public class UserServiceTests { // Inject the real BCryptPasswordEncoder bean from Spring context @Autowired private BCryptPasswordEncoder passwordEncoder; // Replace the real UserRepository with a mock in the Spring context @MockBean private UserRepository userRepository; // Inject the Spring-managed UserService instance @Autowired private UserService userService; @Test void testPasswordEncoding() { String rawPassword = "my-secure-password"; String encodedPassword = passwordEncoder.encode(rawPassword); // Verify the encoding works as expected assert passwordEncoder.matches(rawPassword, encodedPassword); assert !passwordEncoder.matches("wrong-password", encodedPassword); } }
Key Notes for This Setup:
@MockBeantells Spring to create a mock ofUserRepositoryand register it in the context, replacing the real implementation. This lets you testUserServicewith a real password encoder but a mocked repository.- Use this only when you need real Spring-managed beans—unit tests should prefer Solution 1 for speed and isolation.
Final Tips
- Never mix manual instance creation with
@InjectMocks: Let Mockito handle creating the instance for you. - Always configure mocks: Unconfigured mocks return default values, which are almost always the cause of NPEs in Mockito tests.
- Choose the right test type: Unit tests (Mockito only) are faster and more isolated; integration tests (Spring context) are for testing interactions between multiple beans.
内容的提问来源于stack exchange,提问作者Prabagaran Sellamuthu
相关产品推荐
相关产品推荐

