Mockito @InjectMocks注入失效,手动构造正常的问题求助
问题原因及解决方案
常见原因
缺失
passwordEncoder的@Mock声明
你提到已用@Mock声明了tokenService、jwtDecoder,但可能遗漏了passwordEncoder的@Mock注解。@InjectMocks只会注入已被@Mock(或@Spy)标记的实例,未声明的依赖会被留空,调用其方法自然抛出空指针。Kotlin类的构造函数/属性可见性限制
- 如果
MailLoginService的passwordEncoder是private val字段而非构造函数参数,Mockito的反射机制在Kotlin默认的访问控制下无法注入私有字段。 - 若
MailLoginService存在多个构造函数,@InjectMocks可能选择了不包含passwordEncoder的构造函数,导致依赖未注入。
- 如果
Mockito注解未正确初始化
若测试类未通过@ExtendWith(MockitoExtension::class)或MockitoAnnotations.openMocks(this)启动Mockito注解处理,@InjectMocks和@Mock都不会生效,但你手动构造能运行,这个可能性较低。
解决方案
方案1:补全passwordEncoder的@Mock声明
确保所有需要注入的依赖都用@Mock标记,示例代码:
import org.junit.jupiter.api.Test import org.mockito.InjectMocks import org.mockito.Mock import org.mockito.junit.jupiter.MockitoExtension import org.junit.jupiter.api.extension.ExtendWith @ExtendWith(MockitoExtension::class) class MailLoginServiceTest { @Mock lateinit var tokenService: TokenService @Mock lateinit var jwtDecoder: JwtDecoder @Mock // 必须添加这个注解 lateinit var passwordEncoder: PasswordEncoder @InjectMocks lateinit var mailLoginService: MailLoginService @Test fun testPasswordEncoding() { // 测试逻辑,此时passwordEncoder已被注入mock实例 } }
方案2:使用构造函数注入(推荐)
让MailLoginService通过主构造函数接收所有依赖,@InjectMocks会优先使用构造函数注入,避免字段访问权限问题:
// 业务类 class MailLoginService( private val tokenService: TokenService, private val jwtDecoder: JwtDecoder, private val passwordEncoder: PasswordEncoder ) { fun encodePassword(rawPassword: String): String { return passwordEncoder.encode(rawPassword) } }
这种方式无需修改属性可见性,且符合依赖注入的最佳实践,Mockito能可靠地注入所有@Mock实例。
方案3:调整字段可见性(仅当无法使用构造函数注入时)
如果必须用字段注入,可将passwordEncoder标记为internal或添加@JvmField注解,让Mockito能反射访问:
class MailLoginService { @JvmField // 允许Mockito通过反射注入 lateinit var passwordEncoder: PasswordEncoder @JvmField lateinit var tokenService: TokenService @JvmField lateinit var jwtDecoder: JwtDecoder }
内容的提问来源于stack exchange,提问作者Niklas
相关产品推荐
相关产品推荐

