OCT REM: 1. 优化单元测试执行速度

This commit is contained in:
HuangXin 2025-03-31 18:16:57 +08:00
parent 9a28f38e47
commit 169d2e8fa3
2 changed files with 327 additions and 399 deletions

View File

@ -1,14 +1,16 @@
package com.cmcc.magent.misc;
import com.cmcc.magent.common.ErrorCode;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.validation.Validator;
import jakarta.validation.groups.Default;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.mockito.Mock;
import org.springframework.boot.test.context.SpringBootTest;
import org.mockito.MockedStatic;
import org.mockito.MockitoAnnotations;
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
import java.io.ByteArrayInputStream;
@ -26,6 +28,8 @@ import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.mockStatic;
import static org.mockito.Mockito.when;
/**
@ -49,7 +53,6 @@ import static org.mockito.Mockito.when;
* @version 1.0.0
* @since 2025-01-14
*/
@SpringBootTest
@Slf4j
@DisplayName("辅助功能函数")
public class HelperUtilsTest {
@ -60,28 +63,17 @@ public class HelperUtilsTest {
@Mock
private Validator validator;
/**
* 测试 JSON 序列化功能
* <p>
* 验证 {@link JsonUtils#getJson(Object)} 方法是否能正确序列化传入的对象为 JSON 字符串
* </p>
*
* @throws Exception 如果 JSON 序列化失败
*/
@Test
@DisplayName("JSON 序列化")
void testGetJson() throws Exception {
// 创建一个测试对象
Long jsonVal = 123L;
// 调用 getJson 方法
String json = JsonUtils.getJson(jsonVal);
// 断言生成的 JSON 字符串不为空
assertNotNull(json);
assertEquals(new ObjectMapper().writeValueAsString(jsonVal), json);
@BeforeEach
public void setUp() {
MockitoAnnotations.openMocks(this);
}
@AfterEach
public void tearDown() {
}
/**
* 测试字符串截断功能
* <p>
@ -108,6 +100,24 @@ public class HelperUtilsTest {
assertTrue(truncatedString.startsWith(orgString.substring(0, maxLength - 3)));
}
@Test
@DisplayName("字符串截断-长度小于等于最大长度")
void testTruncateString_NotNeedRun() {
// 原始字符串
String orgString = "This is a long string";
// 截断长度
int maxLength = 128;
// 调用 truncateString 方法
String truncatedString = HelperUtils.truncateString(orgString, maxLength);
log.info("[{}] --> [{}]", orgString, truncatedString);
// 断言截断后的字符串长度符合预期
assertEquals(orgString.length(), truncatedString.length());
assertEquals(orgString, truncatedString);
}
/**
* 测试字节数组转换为十六进制字符串功能
* <p>
@ -278,24 +288,39 @@ public class HelperUtilsTest {
@Test
@DisplayName("参数校验")
public void validate_ValidObject_NoViolations() {
Object validObject = new Object(); // 假设这是一个有效的对象
when(validator.validate(validObject)).thenReturn(Collections.emptySet());
try (MockedStatic<ApiContextUtils> apiContextUtilsMocked = mockStatic(ApiContextUtils.class);
MockedStatic<SpringBootBeanUtils> springBootBeanUtilsMocked = mockStatic(SpringBootBeanUtils.class)
) {
apiContextUtilsMocked.when(ApiContextUtils::getLanguage).thenReturn("zh_CN");
springBootBeanUtilsMocked.when(() -> SpringBootBeanUtils.getBean(LocalValidatorFactoryBean.class)).thenReturn(validatorFactory);
Object validObject = new Object(); // 假设这是一个有效的对象
when(validator.validate(validObject)).thenReturn(Collections.emptySet());
when(validatorFactory.getValidator()).thenReturn(validator);
List<String> errors = HelperUtils.validate(validObject);
List<String> errors = HelperUtils.validate(validObject);
assertEquals(Collections.emptyList(), errors);
assertEquals(Collections.emptyList(), errors);
}
}
@Test
@DisplayName("参数校验空集合")
public void validate_ValidObjectWithGroups_NoViolations() {
Object validObject = new Object(); // 假设这是一个有效的对象
when(validator.validate(validObject, Default.class)).thenReturn(Collections.emptySet());
try (MockedStatic<ApiContextUtils> apiContextUtilsMocked = mockStatic(ApiContextUtils.class);
MockedStatic<SpringBootBeanUtils> springBootBeanUtilsMocked = mockStatic(SpringBootBeanUtils.class)
) {
apiContextUtilsMocked.when(ApiContextUtils::getLanguage).thenReturn("zh_CN");
springBootBeanUtilsMocked.when(() -> SpringBootBeanUtils.getBean(LocalValidatorFactoryBean.class)).thenReturn(validatorFactory);
when(validatorFactory.getValidator()).thenReturn(validator);
List<String> errors = HelperUtils.validate(validObject, Default.class);
Object validObject = new Object(); // 假设这是一个有效的对象
when(validator.validate(validObject, Default.class)).thenReturn(Collections.emptySet());
assertEquals(Collections.emptyList(), errors);
List<String> errors = HelperUtils.validate(validObject, Default.class);
assertEquals(Collections.emptyList(), errors);
}
}
@Test
@ -313,12 +338,75 @@ public class HelperUtilsTest {
@Test
@DisplayName("格式化消息")
public void testGetFormatMessage() {
List<String> errors = List.of("error1", "error2");
String[] result = HelperUtils.formatErrorMessage(ErrorCode.ERR_PARAMEXCEPTION, errors);
assertNotNull(result);
assertEquals(3, result.length);
assertEquals("0: " + ErrorCode.ERR_PARAMEXCEPTION.getDescription(), result[0]);
assertEquals("1: error1", result[1]);
assertEquals("2: error2", result[2]);
try (MockedStatic<MessageUtil> mockedMessageUtil = mockStatic(MessageUtil.class)) {
mockedMessageUtil.when(() -> MessageUtil.get(anyString(), anyString())).thenReturn("Error message");
List<String> errors = List.of("error1", "error2");
String[] result = HelperUtils.formatErrorMessage(ErrorCode.ERR_PARAMEXCEPTION, errors);
assertNotNull(result);
assertEquals(3, result.length);
assertEquals("0: " + ErrorCode.ERR_PARAMEXCEPTION.getDescription(), result[0]);
assertEquals("1: error1", result[1]);
assertEquals("2: error2", result[2]);
}
}
@Test
@DisplayName("getField-获取私有字段值")
public void getField_ValidInput_ReturnsFieldValue() throws Exception {
// 准备测试对象
TestObject testObj = new TestObject("testValue");
// 获取私有字段值
String fieldValue = HelperUtils.getField(testObj, "field");
// 验证
assertEquals("testValue", fieldValue);
}
@Test
@DisplayName("getField-字段不存在抛出异常")
public void getField_NonExistingField_ThrowsException() {
TestObject testObj = new TestObject("testValue");
assertThrows(NoSuchFieldException.class, () -> {
HelperUtils.getField(testObj, "nonExistingField");
});
}
@Test
@DisplayName("setField-设置私有字段值")
public void setField_ValidInput_SetsFieldValue() throws Exception {
// 准备测试对象
TestObject testObj = new TestObject("oldValue");
// 设置私有字段值
HelperUtils.setField(testObj, "field", "newValue");
// 验证字段值已更新
assertEquals("newValue", testObj.getField());
}
@Test
@DisplayName("setField-字段不存在抛出异常")
public void setField_NonExistingField_ThrowsException() {
TestObject testObj = new TestObject("testValue");
assertThrows(NoSuchFieldException.class, () -> {
HelperUtils.setField(testObj, "nonExistingField", "newValue");
});
}
// 测试用内部类
private static class TestObject {
private String field;
public TestObject(String field) {
this.field = field;
}
// 用于验证字段值是否被正确设置
public String getField() {
return field;
}
}
}

View File

@ -1,18 +1,22 @@
package com.cmcc.magent.misc;
import com.cmcc.magent.config.ObjectMapperProvider;
import com.cmcc.magent.pojo.po.BaseRespStatus;
import com.cmcc.magent.pojo.po.MemoryDetails;
import com.cmcc.magent.pojo.vo.BasePageResultResp;
import com.cmcc.magent.pojo.vo.ProtocolResp;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import jakarta.servlet.ReadListener;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.servlet.ServletInputStream;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import org.springframework.boot.test.context.SpringBootTest;
import org.mockito.MockedStatic;
import org.mockito.MockitoAnnotations;
import java.io.ByteArrayInputStream;
import java.io.IOException;
@ -22,11 +26,15 @@ import java.lang.reflect.InvocationTargetException;
import java.nio.charset.StandardCharsets;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.mockStatic;
import static org.mockito.Mockito.when;
/**
* 测试类ProtocolJsonUtils 测试
@ -46,365 +54,35 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
* @version 1.0.0
* @since 2025-01-20
*/
@SpringBootTest
@Slf4j
@DisplayName("ProtocolJsonUtils 测试类")
@SuppressWarnings("unchecked")
public class ProtocolJsonUtilsTest {
private static final ObjectMapper mockObjectMapper = new ObjectMapper();
private static MockedStatic<ObjectMapperProvider> objectMapperProviderMock;
private TestObject testObj;
private ServletInputStream servletInputStream;
private ProtocolResp protocolResp;
private ProtocolResp protocolRespB;
private ServletInputStream servletInputStream;
private String validJson;
private String invalidJson;
private String validJsonB;
private String invalidJsonB;
private String emptyJson;
private String validJsonB2;
private String invalidJsonB2;
@BeforeAll
public static void setUpAll() {
objectMapperProviderMock = mockStatic(ObjectMapperProvider.class);
objectMapperProviderMock.when(ObjectMapperProvider::getMapper).thenReturn(mockObjectMapper);
}
@AfterAll
public static void tearDownAll() {
objectMapperProviderMock.close();
}
/**
* 初始化方法在每个测试方法执行之前运行
*
* <p>此方法初始化需要的测试数据</p>
*/
@BeforeEach
@SuppressWarnings("unchecked")
public void setUp() {
protocolResp = new ProtocolResp();
protocolRespB = new ProtocolResp();
protocolRespB.setVer(1);
protocolRespB.setCryptoType(1);
protocolRespB.setTimeStamp(12345L);
protocolRespB.setCode(200);
protocolRespB.setMsgContent(new String[] {"Test Protocol"});
public void setUp() throws IOException {
MockitoAnnotations.openMocks(this);
servletInputStream = Mockito.mock(ServletInputStream.class);
testObj = new TestObject("testValue");
validJson = "{\"code\":\"0000\",\"msg\":\"success\",\"data\":{\"id\":\"12345\",\"name\":\"Test Name\"}}";
invalidJson = "{\"code\":\"0000\",\"msg\":\"success\",\"data\":{}}"; // 缺少字段
validJsonB = "{\"ver\":1,\"cryptoType\":0,\"timeStamp\":1633072800000,\"code\":200,\"msgContent\":{\"status\":0," +
"\"message\":[\"操作成功\"]}}";
invalidJsonB = "{\"ver\":1,\"cryptoType\":0,\"timeStamp\":1633072800000,\"code\":200,\"msgContent\":\"Invalid content\"}";
emptyJson = "";
validJsonB2 = "{\"ver\":1,\"cryptoType\":0,\"timeStamp\":1672531200000,\"code\":200,\"msgContent\":{\"status\":0," +
"\"message\":[\"操作成功\"]}}";
invalidJsonB2 = "{\"ver\":1,\"cryptoType\":0,\"timeStamp\":1672531200000,\"code\":200,\"msgContent\":{\"status\":0}}"; // 缺少
// message 字段
}
/**
* 测试场景对于有效对象getJsonBytes 方法应返回 JSON 字节数组
*
* <p>预期结果返回非空的 JSON 字节数组包含对象的各个字段</p>
*
* @throws JsonProcessingException 如果序列化过程中发生错误
*/
@Test
@DisplayName("有效对象应返回 JSON 字节数组")
public void getJsonBytes_ValidObject_ShouldReturnJsonBytes() throws JsonProcessingException {
byte[] jsonBytes = ProtocolJsonUtils.getJsonBytes(protocolRespB, StandardCharsets.UTF_8);
assertNotNull(jsonBytes);
assertTrue(jsonBytes.length > 0);
String jsonString = new String(jsonBytes, StandardCharsets.UTF_8);
assertTrue(jsonString.contains("ver"));
assertTrue(jsonString.contains("cryptoType"));
assertTrue(jsonString.contains("timeStamp"));
assertTrue(jsonString.contains("code"));
assertTrue(jsonString.contains("msgContent"));
}
/**
* 测试场景对于空对象getJsonBytes 方法应返回空字节数组
*
* <p>预期结果返回表示 "null" 的字节数组</p>
*
* @throws JsonProcessingException 如果序列化过程中发生错误
*/
@Test
@DisplayName("空对象应返回空字节数组")
public void getJsonBytes_NullObject_ShouldReturnEmptyByteArray() throws JsonProcessingException {
byte[] jsonBytes = ProtocolJsonUtils.getJsonBytes(null, StandardCharsets.UTF_8);
assertNotNull(jsonBytes);
assertEquals(4, jsonBytes.length);
assertEquals("null", new String(jsonBytes, StandardCharsets.UTF_8));
}
/**
* 测试场景使用不同字符集时getJsonBytes 方法应返回指定字符集的 JSON 字节数组
*
* <p>预期结果返回的 JSON 字节数组使用指定的字符集编码</p>
*
* @throws JsonProcessingException 如果序列化过程中发生错误
*/
@Test
@DisplayName("不同字符集应返回指定字符集的 JSON 字节数组")
public void getJsonBytes_DifferentCharset_ShouldReturnJsonBytesInSpecifiedCharset() throws JsonProcessingException {
byte[] jsonBytes = ProtocolJsonUtils.getJsonBytes(protocolRespB, StandardCharsets.UTF_8);
assertNotNull(jsonBytes);
assertTrue(jsonBytes.length > 0);
String jsonString = new String(jsonBytes, StandardCharsets.UTF_8);
assertTrue(jsonString.contains("ver"));
assertTrue(jsonString.contains("cryptoType"));
assertTrue(jsonString.contains("timeStamp"));
assertTrue(jsonString.contains("code"));
assertTrue(jsonString.contains("msgContent"));
}
/**
* 测试场景对于空 JSON 字符串jsonGetObject 方法应抛出异常
*
* <p>预期结果抛出 {@link JsonProcessingException}</p>
*/
@Test
@DisplayName("空 JSON 应抛出异常")
public void jsonGetObject_EmptyJson_ThrowsException() {
String emptyJson = "";
assertThrows(JsonProcessingException.class, () -> ProtocolJsonUtils.jsonGetObject(emptyJson, ProtocolResp.class));
}
/**
* 测试场景对于不匹配的 JSON 字符串jsonGetObject 方法应返回默认对象
*
* <p>预期结果返回的对象字段为 null</p>
*
* @throws JsonProcessingException 如果反序列化过程中发生错误
*/
@Test
@DisplayName("不匹配的 JSON 应返回默认对象")
@SuppressWarnings("unchecked")
public void jsonGetObject_MismatchedJson() throws JsonProcessingException {
String mismatchedJson = "{\"protocolId\":12345,\"protocolName\":true,\"protocolType\":null,\"protocolVersion\":1.0," +
"\"protocolStatus\":\"SUCCESS\"}";
ProtocolResp<String> actualProtocolResp = ProtocolJsonUtils.jsonGetObject(mismatchedJson, ProtocolResp.class);
assertNull(actualProtocolResp.getVer());
assertNull(actualProtocolResp.getCryptoType());
assertNull(actualProtocolResp.getTimeStamp());
assertNull(actualProtocolResp.getCode());
assertNull(actualProtocolResp.getMsgContent());
}
/**
* 测试场景对于无效 JSON 字符串jsonGetObject 方法应抛出 IOException
*
* <p>预期结果抛出 {@link IOException}</p>
*
*/
@Test
@DisplayName("无效 JSON 应抛出 IOException")
public void jsonGetObject_InvalidJson_ThrowsIOException() {
String invalidJson = "{\"code\":\"0000\",\"message\":\"success\",\"data\":{\"id\":\"12345678901234567890123456789012\"," +
"\"name\":\"test\",\"status\":\"1\",\"remark\":\"remark\"}";
InputStream inputStream = new ByteArrayInputStream(invalidJson.getBytes());
servletInputStream = new ServletInputStream() {
@Override
public int read() throws IOException {
return inputStream.read();
}
@Override
public boolean isFinished() {
return false;
}
@Override
public boolean isReady() {
return false;
}
@Override
public void setReadListener(ReadListener readListener) {
// No-op
}
};
assertThrows(IOException.class, () -> ProtocolJsonUtils.jsonGetObject(servletInputStream, new TypeReference<ProtocolResp>() {
}));
}
/**
* 测试场景对于空 JSON 对象jsonGetObject 方法应返回默认对象
*
* <p>预期结果返回的对象与默认对象相同</p>
*
* @throws IOException 如果反序列化过程中发生错误
*/
@Test
@DisplayName("空 JSON 对象应返回默认对象")
public void jsonGetObject_EmptyJsonObject_ReturnsDefaultObject() throws IOException {
String json = "{}";
InputStream inputStream = new ByteArrayInputStream(json.getBytes());
servletInputStream = new ServletInputStream() {
@Override
public int read() throws IOException {
return inputStream.read();
}
@Override
public boolean isFinished() {
return false;
}
@Override
public boolean isReady() {
return false;
}
@Override
public void setReadListener(ReadListener readListener) {
// No-op
}
};
ProtocolResp result = ProtocolJsonUtils.jsonGetObject(servletInputStream, new TypeReference<>() {
});
assertEquals(new ProtocolResp(), result);
}
/**
* 测试场景对于有效 JSONB 字符串jsonGetObject 方法应返回 ProtocolResp
*
* <p>预期结果返回的对象包含正确的字段值</p>
*
* @throws JsonProcessingException 如果反序列化过程中发生错误
*/
@Test
@DisplayName("有效 JSONB 应返回 ProtocolResp")
public void jsonGetObject_ValidJsonB_ShouldReturnProtocolResp() throws JsonProcessingException {
String json = "{\"ver\":1,\"cryptoType\":0,\"timeStamp\":1737339486666,\"code\":200,\"msgContent\":{\"items\":{\"pageNumber\":1," +
"\"pageSize\":10,\"totalPage\":1,\"totalRow\":2,\"items\":[{\"totalMemory\":326236233336,\"freeMemory\":132532643}," +
"{\"totalMemory\":2326236233336,\"freeMemory\":2132532643}]},\"status\":0,\"message\":[\"Successful\"]}}";
ProtocolResp<?> resp = ProtocolJsonUtils.jsonGetProtocolResp(json, new Class<?>[] {BasePageResultResp.class, MemoryDetails.class});
assertNotNull(resp);
assertEquals(200, resp.getCode());
}
/**
* 测试场景对于有效 JSON 字符串jsonGetObject 方法应成功反序列化
*
* <p>预期结果返回的对象不为空并包含正确的数据</p>
*
* @throws IOException 如果反序列化过程中发生错误
*/
@Test
@DisplayName("有效 JSON 成功反序列化")
public void jsonGetObject_ValidJson_SuccessfulDeserialization() throws IOException {
ProtocolResp<?> result = ProtocolJsonUtils.jsonGetObject(validJson, new TypeReference<>() {
});
assertNotNull(result);
}
/**
* 测试场景对于空 JSON 字符串jsonGetObject 方法应抛出 IOException
*
* <p>预期结果抛出 {@link IOException}</p>
*/
@Test
@DisplayName("空 JSON 字符串应抛出 IOException")
public void jsonGetObject_EmptyJsonString_ThrowsIOException() {
assertThrows(IOException.class, () -> ProtocolJsonUtils.jsonGetObject("", new TypeReference<ProtocolResp<?>>() {
}));
}
/**
* 测试场景对于有效 JSON 字符串jsonGetProtocolResp 方法应成功反序列化
*
* <p>预期结果返回的对象包含正确的字段值</p>
*
* @throws JsonProcessingException 如果反序列化过程中发生错误
*/
@Test
@DisplayName("有效 JSON 成功反序列化")
public void jsonGetProtocolResp_ValidJson_SuccessfulDeserialization() throws JsonProcessingException {
ProtocolResp<? extends BaseRespStatus> protocolResp = ProtocolJsonUtils.jsonGetProtocolResp(validJsonB, BaseRespStatus.class);
assertNotNull(protocolResp);
assertEquals(1, protocolResp.getVer());
assertEquals(0, protocolResp.getCryptoType());
assertEquals(200, protocolResp.getCode());
assertNotNull(protocolResp.getMsgContent());
assertEquals(0, protocolResp.getMsgContent().getStatus());
}
/**
* 测试场景对于无效 JSON 字符串jsonGetProtocolResp 方法应抛出 JsonProcessingException
*
* <p>预期结果抛出 {@link JsonProcessingException}</p>
*/
@Test
@DisplayName("无效 JSON 应抛出 JsonProcessingException")
public void jsonGetProtocolResp_InvalidJson_ThrowsJsonProcessingException() {
assertThrows(JsonProcessingException.class, () -> ProtocolJsonUtils.jsonGetProtocolResp(invalidJsonB, BaseRespStatus.class));
}
/**
* 测试场景对于空 JSON 字符串jsonGetProtocolResp 方法应抛出 JsonProcessingException
*
* <p>预期结果抛出 {@link JsonProcessingException}</p>
*/
@Test
@DisplayName("空 JSON 应抛出 JsonProcessingException")
public void jsonGetProtocolResp_EmptyJson_ThrowsJsonProcessingException() {
assertThrows(JsonProcessingException.class, () -> ProtocolJsonUtils.jsonGetProtocolResp(emptyJson, BaseRespStatus.class));
}
/**
* 测试场景对于有效 JSON 字符串jsonGetProtocolResp 方法应返回 ProtocolResp
*
* <p>预期结果返回的对象包含正确的字段值</p>
*
* @throws JsonProcessingException 如果反序列化过程中发生错误
*/
@Test
@DisplayName("有效 JSON 应返回 ProtocolResp")
public void jsonGetProtocolResp_ValidJson_ShouldReturnProtocolResp() throws JsonProcessingException {
ProtocolResp<? extends BaseRespStatus> result = ProtocolJsonUtils.jsonGetProtocolResp(validJsonB2,
new Class<?>[] {BaseRespStatus.class});
assertEquals(1, result.getVer());
assertEquals(0, result.getCryptoType());
assertEquals(1672531200000L, result.getTimeStamp());
assertEquals(200, result.getCode());
assertEquals(0, result.getMsgContent().getStatus());
assertEquals("操作成功", result.getMsgContent().getMessage()[0]);
}
/**
* 测试场景对于嵌套泛型的 JSON 字符串jsonGetProtocolResp 方法应正确处理
*
* <p>预期结果返回的对象中的消息字段包含多个条目</p>
*
* @throws JsonProcessingException 如果反序列化过程中发生错误
*/
@Test
@DisplayName("嵌套泛型应正确处理")
public void jsonGetProtocolResp_NestedGenerics_ShouldHandleCorrectly() throws JsonProcessingException {
String nestedJson = "{\"ver\":1,\"cryptoType\":0,\"timeStamp\":1672531200000,\"code\":200," +
"\"msgContent\":{\"status\":0,\"message\":[\"操作成功\",\"另一个消息\"]}}";
ProtocolResp<? extends BaseRespStatus> result = ProtocolJsonUtils.jsonGetProtocolResp(nestedJson,
new Class<?>[] {BaseRespStatus.class});
assertEquals(2, result.getMsgContent().getMessage().length);
assertEquals("另一个消息", result.getMsgContent().getMessage()[1]);
}
/**
* 测试场景getJsonBytes 方法应成功返回对象的 JSON 字节数组
*
* <p>预期结果返回的字节数组不为空</p>
*
* @throws JsonProcessingException 如果序列化过程中发生错误
*/
@Test
@DisplayName("getJsonBytes 应成功")
public void jsonGetProtocolResp_GetJsonBytes_ShouldSuccessfully() throws JsonProcessingException {
MemoryDetails memoryDetails = new MemoryDetails();
memoryDetails.setTotalMemory(1234567890L);
memoryDetails.setFreeMemory(987654321L);
byte[] ret = ProtocolJsonUtils.getJsonBytes(memoryDetails);
assertNotEquals(0, ret.length);
// 模拟ServletInputStream
ByteArrayInputStream inputStream = new ByteArrayInputStream("{\"field\":\"testValue\"}".getBytes(StandardCharsets.UTF_8));
servletInputStream = mock(ServletInputStream.class);
when(servletInputStream.read()).thenAnswer(invocation -> inputStream.read());
}
@ -414,8 +92,170 @@ public class ProtocolJsonUtilsTest {
assertThrows(InvocationTargetException.class, () -> {
Constructor<ProtocolJsonUtils> constructor = ProtocolJsonUtils.class.getDeclaredConstructor();
constructor.setAccessible(true);
ProtocolJsonUtils instance = constructor.newInstance();
assertNotNull(instance);
constructor.newInstance();
});
}
@Test
@DisplayName("对象序列化为字节数组")
public void getJsonBytes_ValidObject_ReturnsBytes() throws JsonProcessingException {
byte[] result = ProtocolJsonUtils.getJsonBytes(testObj);
assertNotNull(result);
assertTrue(result.length > 0);
}
@Test
@DisplayName("指定字符集序列化")
public void getJsonBytes_WithCharset_ReturnsBytes() throws JsonProcessingException {
byte[] result = ProtocolJsonUtils.getJsonBytes(testObj, StandardCharsets.UTF_8);
assertNotNull(result);
assertTrue(result.length > 0);
}
@Test
@DisplayName("从字符串反序列化")
public void jsonGetObject_FromString_ReturnsObject() throws JsonProcessingException {
String json = "{\"field\":\"testValue\"}";
TestObject result = ProtocolJsonUtils.jsonGetObject(json, TestObject.class);
assertEquals("testValue", result.field());
}
@Test
@DisplayName("从输入流反序列化")
public void jsonGetObject_FromInputStream_ReturnsObject() throws IOException {
String json = "{\"field\":\"testValue\"}";
TypeReference<TestObject> typeReference = new TypeReference<>() {
};
try (MockedStatic<HelperUtils> helperUtilsMock = mockStatic(HelperUtils.class)
) {
helperUtilsMock.when(() -> HelperUtils.inputStream2String(any(InputStream.class))).thenReturn(json);
TestObject result = ProtocolJsonUtils.jsonGetObject(servletInputStream, typeReference);
assertEquals("testValue", result.field());
}
}
@Test
@DisplayName("从字符串反序列化(TypeReference)")
public void jsonGetObject_FromStringWithType_ReturnsObject() throws IOException {
String json = "{\"field\":\"testValue\"}";
TypeReference<TestObject> typeReference = new TypeReference<>() {
};
TestObject result = ProtocolJsonUtils.jsonGetObject(json, typeReference);
assertEquals("testValue", result.field());
}
@Test
@DisplayName("基本类型反序列化")
public void jsonGetProtocolResp_SimpleType_ReturnsProtocolResp() throws JsonProcessingException {
String json = "{\"ver\":1,\"cryptoType\":0,\"timeStamp\":12345,\"code\":200,\"msgContent\":{\"status\":0,\"message\":[\"成功\"]}}";
try (MockedStatic<MessageUtil> messageUtilMock = mockStatic(MessageUtil.class)) {
messageUtilMock.when(() -> MessageUtil.get(anyString(), anyString())).thenReturn("成功");
BaseRespStatus status = new BaseRespStatus(0, new String[] {"成功"});
ProtocolResp<BaseRespStatus> resp = new ProtocolResp<>();
resp.setVer(1);
resp.setCryptoType(0);
resp.setTimeStamp(12345L);
resp.setCode(200);
resp.setMsgContent(status);
ProtocolResp<? extends BaseRespStatus> result = ProtocolJsonUtils.jsonGetProtocolResp(json, BaseRespStatus.class);
assertEquals(200, result.getCode());
assertEquals(0, result.getMsgContent().getStatus());
}
}
@Test
@DisplayName("嵌套泛型反序列化")
public void jsonGetProtocolResp_NestedGenerics_ReturnsProtocolResp() throws JsonProcessingException {
String json = "{\"ver\":1,\"cryptoType\":0,\"timeStamp\":12345,\"code\":200,\"msgContent\":{\"status\":0,\"message\":[\"成功\"]}}";
try (MockedStatic<MessageUtil> messageUtilMock = mockStatic(MessageUtil.class)) {
messageUtilMock.when(() -> MessageUtil.get(anyString(), anyString())).thenReturn("成功");
BaseRespStatus status = new BaseRespStatus(0, new String[] {"成功"});
ProtocolResp<BaseRespStatus> resp = new ProtocolResp<>();
resp.setVer(1);
resp.setCryptoType(0);
resp.setTimeStamp(12345L);
resp.setCode(200);
resp.setMsgContent(status);
ProtocolResp<? extends BaseRespStatus> result = ProtocolJsonUtils.jsonGetProtocolResp(json,
new Class<?>[] {BaseRespStatus.class});
assertEquals(200, result.getCode());
assertEquals(0, result.getMsgContent().getStatus());
}
}
@Test
@DisplayName("无效JSON抛出异常")
public void jsonGetObject_InvalidJson_ThrowsException() {
String invalidJson = "{invalid json}";
assertThrows(JsonProcessingException.class, () -> {
ProtocolJsonUtils.jsonGetObject(invalidJson, TestObject.class);
});
}
@Test
@DisplayName("从字符串反序列化(TypeReference嵌套)")
void jsonGetProtocolResp_shouldHandleTreeLevelNesting() throws Exception {
try (MockedStatic<MessageUtil> messageUtilMock = mockStatic(MessageUtil.class)) {
messageUtilMock.when(() -> MessageUtil.get(anyString(), anyString())).thenReturn("testMessage");
// 准备测试数据三层嵌套
Class<?>[] classes = {NestedResp.class, TestStatus.class, LastStatus.class};
LastStatus lastStatus = new LastStatus();
lastStatus.setStatus(0);
lastStatus.setMessage(new String[] {"LastStatus"});
TestStatus<LastStatus> testStatus = new TestStatus<>();
testStatus.setStatus(1);
testStatus.setMessage(new String[] {"TestStatus"});
testStatus.setData(lastStatus);
NestedResp<TestStatus<LastStatus>> nestedResp = new NestedResp<>();
nestedResp.setStatus(2);
nestedResp.setMessage(new String[] {"NestedResp"});
nestedResp.setData(testStatus);
ProtocolResp<NestedResp<TestStatus<LastStatus>>> ret = new ProtocolResp<>();
ret.setVer(1);
ret.setCryptoType(0);
ret.setTimeStamp(12345L);
ret.setCode(200);
ret.setMsgContent(nestedResp);
String objJson = mockObjectMapper.writeValueAsString(ret);
log.info("++++{}: {}", mockObjectMapper.hashCode(), objJson);
// 执行方法
ProtocolResp<? extends BaseRespStatus> result = ProtocolJsonUtils.jsonGetProtocolResp(objJson, classes);
// 验证嵌套结构
assertInstanceOf(NestedResp.class, result.getMsgContent());
NestedResp<?> nested = (NestedResp<?>) result.getMsgContent();
assertEquals(TestStatus.class, nested.getData().getClass());
}
}
// 测试用内部类
private record TestObject(String field) {
}
@Setter
@Getter
private static class NestedResp<T> extends BaseRespStatus {
private T data;
}
@Setter
@Getter
static class TestStatus<T> extends BaseRespStatus {
private T data;
}
static class LastStatus extends BaseRespStatus {
}
}