背景
想重新执行下 以前写的 mock 测试类,发生了一堆的问题,进行部分的梳理和深究。
1、执行mock方法时 异常
org.mockito.exceptions.base.MockitoException:
Mockito cannot mock this class: class com.zenlayer.delivery.biz.service.ConfigService.
Mockito can only mock non-private & non-final classes.
If you're not sure why you're getting this error, please report to the mailing list.
Java : 11
JVM vendor name : JetBrains s.r.o
JVM vendor version : 11.0.4+10-b304.77
JVM name : OpenJDK 64-Bit Server VM
JVM version : 11.0.4+10-b304.77
JVM info : mixed mode
OS name : Mac OS X
OS version : 10.15.6
Underlying exception : java.lang.UnsupportedOperationException: Cannot define class using reflection
.....
Caused by: java.lang.IllegalStateException: Could not find sun.misc.Unsafe
at net.bytebuddy.dynamic.loading.ClassInjector$UsingUnsafe$Dispatcher$Disabled.initialize(ClassInjector.java:1366)
......
Caused by: java.lang.NoSuchMethodException: sun.misc.Unsafe.defineClass(java.lang.String, [B, int, int, java.lang.ClassLoader, java.security.ProtectionDomain)
......
... 58 more
编译时 JDK 版本变成 11了
修改:idea-->ProjectStructures-->Modules (语法检测)
2、mock 测试 不成功
mock的方法在 目标方法中未生效。
@Mock @InjectMocks @MockBean 使用有点乱。需要理理思绪!!!
/**
* @Mock 生效
*/
//@RunWith(org.mockito.runners.MockitoJUnitRunner.class) //
/**
* @RunWith(SpringJUnit4ClassRunner.class) + init
* @Mock 不生效
*/
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest
public class ReservedServiceMockTest {
@Autowired
@InjectMocks
private ReservdServerService reservdServerService;
// @Mock
// @Autowired
// @MockBean
@Mock
private ConfigService configService;
// @Autowired
// @MockBean
@Mock
private OrderManager orderManager;
/**
* @Mock 在当前测试 方法直接调用中生效, 但在 目标类中调用 无效
* @MockBean 能 实现。 Springboot 中,使用 @MockBean 加入到 容器中
*/
@Before
public void init() {
MockitoAnnotations.initMocks(this);
}
@Test
public void test_check_reserved() throws JsonProcessingException {
DeliveryOrderDO order = new DeliveryOrderDO();
order.setOrderId("test_check_1");
OrderExtendParam param = new OrderExtendParam();
param.setTargetCount(3);
param.setReservedTotalCount(2);
param.setThisOrderReservedCount(2);
order.setExtendParam(JsonUtils.writeValueAsString(param));
InfoConfig infoConfig = new InfoConfig();
BasicConfig basicConfig = new BasicConfig();
basicConfig.setZoneId(2);
basicConfig.setModelId(2);
basicConfig.setCid(3345);
infoConfig.setBasicConfig(basicConfig);
Mockito.when(configService.getInfoConfig(any())).thenReturn(infoConfig);
InfoConfig infoConfig1 = configService.getInfoConfig("1");
Mockito.when(orderManager.update(any())).thenReturn(true);
reservdServerService.doReserveServer(order);
System.out.println("over!!!");
}
}
现象:
@Mock
在当前测试方法直接调用,mock 生效
目标类中 mock 没生效
查看service类:并不是同一个
@MockBean
调用mock都生效成功。
实践中的一些点记录:
1、@RunWith(SpringJUnit4ClassRunner.class) + @SpringBootTest + MockitoAnnotations.initMocks(this); 模式下,@Mock 在当前test 方法中生效,到 目标类方法中 mock 失效。
> 在spring 容器中 的 service 和mock的service 不是同一个对象导致。
2、使用 @MockBean 注解, 都生效(放入 spring IOC中)
> 使mock的bean 放入 spring IOC中,即 mock的对象和容器中的对象时同一个
3、@RunWith(org.mockito.runners.MockitoJUnitRunner.class) 注解下,使用@InjectMocks和 @Mock 注解 ,在 目标类方法中 mock成功。(非 spring 容器环境)
进一步理解:
1、@Mock 和 @InjectMocks 一起 使用。 @RunWith(org.mockito.runners.MockitoJUnitRunner.class) 初始化了 @Mock 和@InjectMocks。
在 一个service中,需要调用 多个 其他service的时候,要么对 所有的 service进行mock,可以生效,如果(在spring 容器中) 只想对某两个 service进行mock, 其他 正常调用的话,需要把 mock的 service 放入到 Spring容器中才行,即直接使用 @MockBean即可。
2、@SpringBootTest 下,使用 @MockBean(org.springframework.boot.test.mock.mockito
包下) 即可
3、在使用中,需要根据 测试类的场景,进行合理的选择使用 @Mock,@InjectMocks,@MockBean 这些注解,让 测试和执行更高效。
参考:
Spring Boot - Unit Testing and Mocking with Mockito and JUnit