Java 新手入门:Java单元测试利器,Mock详解_java mock-CSDN博客
这个是典型的before when assert三段式,学一下单测思路
这个没有动态代理,所以是直接class(对比下面)
Jmockit使用笔记_增加代码覆盖率_覆盖try catch_使用new MockUp私有方法-CSDN博客
new mock up:
Jmockit使用@MockUp控制被注入Service的方法_mockup使用教程-CSDN博客
默认方法的含义
在 JMockit 中,当一个类的依赖(如 链接的UserService
)被注入时,如果没有为该依赖的某个方法设置具体的行为,JMockit 会提供一个默认实现。这个默认实现通常是:
- 对于 返回值类型 的方法:
- 返回值类型是
int
或其他基本类型时,返回其默认值(如0
、false
等)。 - 返回值类型是对象时,返回
null
。
- 返回值类型是
- 对于
void
方法:- 不执行任何实际逻辑,相当于一个空实现。
由于 userService
是被 JMockit 模拟的,setUserId
方法的默认实现 不会修改 orderInfo
的 userId
属性。因此:
orderInfo.getUserId()
始终为null
模拟类里面的方法:
方式一:使用new Expectations()
@Test
public void getOrderInfo1() {
new Expectations() {{
userService.setUserId((OrderInfo) any);
result = new Delegate<OrderInfo>() {
void delegate(OrderInfo orderInfo) {
orderInfo.setUserId("ZHANGSAN123"); // 模拟方法逻辑
}
};
}};
OrderInfo result = testService.getOrderInfo("123");
// 验证结果
Assert.assertEquals("ZHANGSAN123", result.getUserId());
}
这部分代码是 JMockit 的 Expectations
定义,主要用来模拟依赖对象 userService
的行为。
-
userService.setUserId((OrderInfo) any)
声明当userService
的setUserId
方法被调用时,不管传入的参数是什么((OrderInfo) any
),都会执行后续的行为。 -
result = new Delegate<OrderInfo>()
为被调用的方法定义了一个代理逻辑(即模拟实现)。这里Delegate
是 JMockit 提供的功能,用来自定义方法的执行逻辑。 -
代理逻辑:
void delegate(OrderInfo orderInfo)
- 传入的
orderInfo
对象是调用setUserId
方法时的参数。 - 在代理逻辑中,直接对
orderInfo
的userId
属性赋值为"ZHANGSAN123"
。
- 传入的
解读:
-
userService.setUserId((OrderInfo) any)
定义了userService
的setUserId
方法在接收到任何OrderInfo
对象作为参数时,会执行以下逻辑。 -
result = new Delegate<OrderInfo>() { ... }
通过Delegate
为方法提供了具体的代理逻辑:- 当
setUserId
被调用时,不会执行真实的实现,而是执行代理逻辑,直接将orderInfo.userId
设置为"ZHANGSAN123"
。
- 当
Expectations
如何影响依赖对象?
示例:
OrderInfo result = testService.getOrderInfo("123");
-
依赖方法调用:
testService.getOrderInfo
会调用userService.setUserId(orderInfo)
。
-
方法替换:
- 因为用
Expectations
模拟了setUserId
方法,实际调用的不是UserService
的真实方法,而是Expectations
中定义的代理逻辑。
- 因为用
-
逻辑执行:
- 在代理逻辑中,将
orderInfo.userId
设置为"ZHANGSAN123"
。
- 在代理逻辑中,将
-
结果返回:
- 通过修改后的
OrderInfo
对象返回给调用方。
- 通过修改后的
基本原理Expectations
是 JMockit 提供的一种基于 声明式 的方式,用于定义某个依赖对象的方法行为。
- 通过在测试代码中定义具体的行为,测试框架根据定义的规则动态返回结果或执行逻辑。
- 对特定实例生效,不影响该类的其他实例。
方式2:使用new mockup()推荐:更easy
new MockUp<UserService>(userService.getClass()) {
@Mock
public void setUserId(OrderInfo orderInfo) {
orderInfo.setUserId("zhangsan123");
}
};
基本原理MockUp
是 JMockit 提供的一种方式,用于在运行时修改类的方法实现。
- 可以直接为某个类的具体方法编写新的实现,类似于 重写方法。
- 使用
@Mock
注解标记需要模拟的方法。
注意:
/**
* @author xinruoxiangyang9010
* 这里的参数必须是:userService.getClass()
* 如果写成UserService.class则@Mock里面的方法不生效
*/
new MockUp<UserService>(userService.getClass())
1. UserService.class
- 表示类级别的代理:
当你使用new MockUp<UserService>()
或new MockUp<UserService>(UserService.class)
时,JMockit 会针对UserService
类本身 进行代理。- 它的效果是全局生效,影响所有实例。
- 但是,如果在运行时,你注入的是
userService
的一个动态代理对象(比如 Spring 的动态代理机制生成的对象),代理逻辑不会直接生效。
2. userService.getClass()
- 表示对象所属的动态类型:
userService.getClass()
返回的是userService
实际运行时的类型。- 如果
userService
是被 Spring 动态代理(如 CGLIB 或 JDK 动态代理)生成的对象,那么userService.getClass()
返回的就是这个动态代理类。 MockUp
会针对这个动态代理类生效,从而让@Mock
标注的方法在调用时起作用。
- 如果
在实际项目中,特别是使用 Spring 框架时,@Autowired
注入的 userService
很可能是一个动态代理对象,而不是直接的 UserService
实例。
假设 userService
是通过 Spring 注入的动态代理对象:
System.out.println(userService.getClass()); // 输出类似 com.example.UserService$$EnhancerBySpringCGLIB$$12345