Mockito原理剖析—无默认构造函数Mock原理

依赖:objenesisbytebuddy

过程:

对于Hotspot默认使用的是SunReflectionFactoryInstantiator,该方式会动态生成一个工厂类,通过该工厂类可以实现类似于默认构造函数的功能:

1、生成一个实现MagicAccessorImpl类的ConstructorAccessor

2、构造一个Constructor对象,设置ConstructorAccessor,当调用Constuctor#newInstance方法,创建对象的行为会委托至ConstructorAccessor#newInstance方法

创建目标类型的流程如下:

1、通过new指令创建目标类型的对象

2、调用Object.<init>初始化方法

该方法的原理是利用了JVM会对MagicAccessorImpl类进行检测,实现了MagicAccessorImpl的子类可以拥有下面特权:

1、绕过类加载时验证阶段:因为初始化方法都是使用Object的初始化方法,但是在验证阶段会校验初始化方法是否存在于直接父类,对于存在继承的类,而且父类不存在默认构造函数的情况,无法通过验证

2、绕过链接时的privateprotectedpackage受限访问检查

### 问题分析 `org.mockito.exceptions.misusing.InjectMocksException: Cannot instantiate @InjectMocks field` 是由于 Mockito 尝试通过反射创建一个对象实例失败所引发的异常。具体来说,当 `@InjectMocks` 注解被用于标记某个字段时,Mockito 需要能够成功初始化该字段对应的类。如果目标是一个接口或者缺少必要的依赖注入,则会抛出此异常。 以下是详细的解决方案及其背景: --- ### 报错原因解析 1. **目标类型是接口而非实现类** 如果使用了 `@InjectMocks` 来标注一个接口类型的字段(如 `VehicleMapper`),而未提供具体的实现类,则 Mockito 无法为其创建实例[^1]。 2. **缺乏显式的实例化声明** 当使用 `@InjectMocks` 时,默认情况下 Mockito 会尝试自动构建指定的对象。但如果开发者没有正确配置 Mock 对象或忘记调用 `MockitoAnnotations.openMocks()` 或者旧版中的 `MockitoAnnotations.initMocks(this)` 方法,则可能导致初始化失败[^3]。 3. **依赖项缺失** 即使提供了实现类,如果没有为所需的依赖属性设置合适的 Mock 实例(通常通过 `@Mock` 定义并由框架管理),也会导致注入过程出现问题。 --- ### 解决方案 #### 方案一:替换为目标的具体实现类 确保 `@InjectMocks` 所修饰的变量指向的是实际可实例化的类而不是抽象类或接口。例如: ```java // 错误示范 - VehicleMapper 是接口 @InjectMocks private VehicleMapper baseMapper; // 正确做法 - 使用接口的具体实现类 MyVehicleMapper @InjectMocks private MyVehicleMapper myVehicleMapper; ``` #### 方案二:手动完成实例化操作 对于某些特殊场景下可以自行定义好待测试的服务对象再交给注解处理: ```java @Service public class ExampleService { private final VehicleMapper vehicleMapper; public ExampleService(VehicleMapper vehicleMapper){ this.vehicleMapper = vehicleMapper; } } @TestInstance(TestInstance.Lifecycle.PER_CLASS) class TestClass{ @Mock VehicleMapper mockMapper; @InjectMocks ExampleService exampleService=new ExampleService(mockMapper); } ``` 注意这里我们直接传入了一个已经存在的 Mapper 替代品给服务层构造函数。 #### 方案三:启用正确的上下文支持 确认当前单元测试环境中已激活相应的工具链来辅助模拟行为建立工作流。比如 Java8 及以上版本推荐采用如下方式开启 session: ```java @BeforeEach void setUp(){ MockitoSessionBuilder builder=Mockito.mockitoSession(); try(MockingProgress progress=builder.startStrictMocking()){ // Your test logic here... }catch(Exception e){/* Handle exception */} } ``` 另外还需记得适时引入额外扩展包以增强功能覆盖范围(如 spring-boot-starter-test 中自带的相关组件)[^2]. --- ### 总结代码片段展示最佳实践 下面给出一段综合运用上述技巧后的完整样例供参考学习: ```java import static org.mockito.Mockito.*; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.MockitoAnnotations; public class SampleTest { @Mock private VehicleMapper vehicleMapper; @InjectMocks private MyServiceImpl serviceImpl = new MyServiceImpl(); @BeforeEach void init() { MockitoAnnotations.openMocks(this); } @Test void shouldReturnExpectedResultWhenMethodCalled() throws Exception { when(vehicleMapper.findVehicles()).thenReturn(Collections.emptyList()); List<Vehicle> result = serviceImpl.getAllVehicles(); verify(vehicleMapper).findVehicles(); assertNotNull(result); } } ``` --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

木子的木木

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值