背景
想重新執行下 以前寫的 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
