refactor(model): 重构模型类包结构并优化序列化处理
重构模型类包结构,将模型类按功能模块划分到不同的子包中。优化序列化处理,为VO类添加serialVersionUID并实现Serializable接口。移除部分冗余的serialVersionUID字段,简化模型类代码。同时修复UITools中空值处理的问题,并更新pom版本至0.0.100-SNAPSHOT。 - 将模型类按功能模块划分到ds子包中 - 为VO类添加序列化支持 - 移除冗余的serialVersionUID字段 - 修复UITools空值处理问题 - 更新项目版本号
This commit is contained in:
@@ -0,0 +1,208 @@
|
||||
package com.ecep.contract.ds.contract.service;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyLong;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.reset;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.test.mock.mockito.MockBean;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
|
||||
import com.ecep.contract.IEntityService;
|
||||
import com.ecep.contract.QueryService;
|
||||
import com.ecep.contract.ds.contract.repository.ContractRepository;
|
||||
import com.ecep.contract.ds.contract.model.Contract;
|
||||
import com.ecep.contract.model.ContractType;
|
||||
import com.ecep.contract.ds.project.model.Project;
|
||||
import com.ecep.contract.vo.ContractVo;
|
||||
import com.ecep.contract.service.BaseServiceTest;
|
||||
import com.ecep.contract.service.VoableService;
|
||||
|
||||
/**
|
||||
* ContractService的集成测试类,用于验证ContractService的实体-VO转换和缓存逻辑
|
||||
*/
|
||||
@SpringBootTest
|
||||
class ContractServiceIntegrationTest extends BaseServiceTest<Contract, ContractVo> {
|
||||
|
||||
@MockBean
|
||||
private ContractRepository contractRepository;
|
||||
|
||||
@MockBean
|
||||
private RedisTemplate<String, Object> redisTemplate;
|
||||
|
||||
@InjectMocks
|
||||
private ContractService contractService;
|
||||
|
||||
@Override
|
||||
protected IEntityService<Contract> getEntityService() {
|
||||
return contractService;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected QueryService<ContractVo> getQueryService() {
|
||||
return contractService;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected VoableService<Contract, ContractVo> getVoableService() {
|
||||
return contractService;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Contract createMockEntity() {
|
||||
Contract contract = new Contract();
|
||||
contract.setId(getMockId());
|
||||
contract.setName("测试合同");
|
||||
contract.setCode("CONTRACT-001");
|
||||
|
||||
// 创建并设置项目对象
|
||||
Project project = new Project();
|
||||
project.setId(1);
|
||||
contract.setProject(project);
|
||||
|
||||
// 创建并设置合同类型对象
|
||||
ContractType contractType = new ContractType();
|
||||
contractType.setId(1);
|
||||
contract.setType(contractType);
|
||||
|
||||
contract.setAmount(10000.0);
|
||||
contract.setState("B"); // 根据Contract类中的注释,B是最常见的状态
|
||||
return contract;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ContractVo createMockVo() {
|
||||
Contract entity = createMockEntity();
|
||||
return entity.toVo();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getRedisKeyPrefix() {
|
||||
return "contract:";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Integer getMockId() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getSearchText() {
|
||||
return "测试";
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试ContractService特有的findById方法,验证实体-VO转换和缓存逻辑
|
||||
*/
|
||||
@Test
|
||||
void testContractFindById() {
|
||||
Integer id = getMockId();
|
||||
Contract mockEntity = createMockEntity();
|
||||
ContractVo expectedVo = createMockVo();
|
||||
|
||||
// 模拟Redis缓存未命中和服务方法调用
|
||||
when(redisTemplate.opsForValue().get(getRedisKeyPrefix() + id)).thenReturn(null);
|
||||
when(contractRepository.findById(id)).thenReturn(java.util.Optional.of(mockEntity));
|
||||
|
||||
// 执行方法
|
||||
ContractVo result = contractService.findById(id);
|
||||
|
||||
// 验证结果
|
||||
assertNotNull(result);
|
||||
assertEquals(expectedVo.getId(), result.getId());
|
||||
assertEquals(expectedVo.getName(), result.getName());
|
||||
assertEquals(expectedVo.getCode(), result.getCode());
|
||||
|
||||
// 验证方法调用
|
||||
verify(redisTemplate, times(1)).opsForValue().get(getRedisKeyPrefix() + id);
|
||||
verify(contractRepository, times(1)).findById(id);
|
||||
verify(redisTemplate, times(1)).opsForValue().set(eq(getRedisKeyPrefix() + id), eq(result), anyLong(), any());
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试ContractService特有的updateByVo方法,验证特定字段的更新逻辑
|
||||
*/
|
||||
@Test
|
||||
void testContractUpdateByVo() {
|
||||
Integer id = getMockId();
|
||||
Contract mockEntity = createMockEntity();
|
||||
ContractVo mockVo = createMockVo();
|
||||
mockVo.setName("更新后的合同名称");
|
||||
mockVo.setAmount(20000.0);
|
||||
|
||||
// 模拟getById方法调用
|
||||
when(contractRepository.findById(id)).thenReturn(java.util.Optional.of(mockEntity));
|
||||
when(contractRepository.save(any(Contract.class))).thenAnswer(i -> i.getArguments()[0]);
|
||||
|
||||
// 执行方法
|
||||
contractService.updateByVo(mockEntity, mockVo);
|
||||
|
||||
// 验证结果 - 直接检查传入的实体对象是否被正确更新
|
||||
assertNotNull(mockEntity);
|
||||
assertEquals(mockVo.getName(), mockEntity.getName());
|
||||
assertEquals(mockVo.getAmount(), mockEntity.getAmount());
|
||||
// code字段应该没有被修改,因为我们使用的是传入的mockEntity的code
|
||||
assertEquals("CONTRACT-001", mockEntity.getCode()); // 未修改的字段保持不变
|
||||
|
||||
// 验证方法调用
|
||||
verify(contractRepository, times(1)).findById(id);
|
||||
verify(contractRepository, times(1)).save(mockEntity);
|
||||
verify(redisTemplate, times(1)).delete(getRedisKeyPrefix() + id);
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试ContractService的缓存刷新机制
|
||||
*/
|
||||
@Test
|
||||
void testContractCacheRefresh() {
|
||||
Integer id = getMockId();
|
||||
Contract mockEntity1 = createMockEntity();
|
||||
Contract mockEntity2 = createMockEntity();
|
||||
mockEntity2.setName("刷新后的合同名称");
|
||||
|
||||
// 第一次调用:缓存未命中
|
||||
when(redisTemplate.opsForValue().get(getRedisKeyPrefix() + id)).thenReturn(null);
|
||||
when(contractRepository.findById(id)).thenReturn(java.util.Optional.of(mockEntity1));
|
||||
|
||||
ContractVo result1 = contractService.findById(id);
|
||||
assertEquals(mockEntity1.getName(), result1.getName());
|
||||
|
||||
// 模拟实体更新
|
||||
reset(redisTemplate);
|
||||
when(contractRepository.findById(id)).thenReturn(java.util.Optional.of(mockEntity2));
|
||||
when(contractService.findById(id)).thenCallRealMethod();
|
||||
when(redisTemplate.opsForValue().get(getRedisKeyPrefix() + id)).thenReturn(null);
|
||||
|
||||
// 第二次调用:应该获取到更新后的数据
|
||||
ContractVo result2 = contractService.findById(id);
|
||||
assertEquals(mockEntity2.getName(), result2.getName());
|
||||
|
||||
// 验证缓存被更新
|
||||
verify(redisTemplate, times(1)).opsForValue().set(eq(getRedisKeyPrefix() + id), eq(result2), anyLong(), any());
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试ContractService的批量操作后缓存清理
|
||||
*/
|
||||
@Test
|
||||
void testContractBatchOperationCacheEviction() {
|
||||
// 模拟批量更新操作
|
||||
Contract mockEntity = createMockEntity();
|
||||
when(contractRepository.save(any(Contract.class))).thenReturn(mockEntity);
|
||||
|
||||
// 执行保存操作
|
||||
contractService.save(mockEntity);
|
||||
|
||||
// 验证缓存被清理
|
||||
verify(redisTemplate, times(1)).delete(anyString());
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,6 @@ import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
@@ -26,7 +25,7 @@ import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.jpa.domain.Specification;
|
||||
|
||||
import com.ecep.contract.ds.contract.repository.ContractRepository;
|
||||
import com.ecep.contract.model.Contract;
|
||||
import com.ecep.contract.ds.contract.model.Contract;
|
||||
import com.ecep.contract.vo.ContractVo;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
@@ -3,14 +3,16 @@ package com.ecep.contract.ds.sale.service;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
import static org.mockito.Mockito.any;
|
||||
import static org.mockito.Mockito.eq;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.ArgumentMatchers.anyLong;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.reset;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.reset;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.Arrays;
|
||||
@@ -22,62 +24,113 @@ import org.junit.jupiter.api.Test;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.test.mock.mockito.MockBean;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageImpl;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.jpa.domain.Specification;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
|
||||
import com.ecep.contract.IEntityService;
|
||||
import com.ecep.contract.QueryService;
|
||||
import com.ecep.contract.ds.contract.repository.SalesOrderRepository;
|
||||
import com.ecep.contract.ds.contract.service.SaleOrdersService;
|
||||
import com.ecep.contract.model.SalesOrder;
|
||||
import com.ecep.contract.ds.customer.model.SalesOrder;
|
||||
import com.ecep.contract.service.BaseServiceTest;
|
||||
import com.ecep.contract.service.VoableService;
|
||||
import com.ecep.contract.vo.SalesOrderVo;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
/**
|
||||
* SaleOrdersService的测试类,用于验证实体-VO转换机制和缓存策略
|
||||
* SaleOrdersService的集成测试类,用于验证SaleOrdersService的实体-VO转换和缓存逻辑
|
||||
*/
|
||||
public class SaleOrdersServiceIntegrationTest {
|
||||
@SpringBootTest
|
||||
public class SaleOrdersServiceIntegrationTest extends BaseServiceTest<SalesOrder, SalesOrderVo> {
|
||||
|
||||
@Mock
|
||||
private SalesOrderRepository repository;
|
||||
|
||||
@MockBean
|
||||
private RedisTemplate<String, Object> redisTemplate;
|
||||
|
||||
@InjectMocks
|
||||
private SaleOrdersService service;
|
||||
|
||||
private ObjectMapper objectMapper;
|
||||
private SalesOrder mockSalesOrder;
|
||||
private SalesOrderVo expectedVo;
|
||||
|
||||
@Override
|
||||
protected IEntityService<SalesOrder> getEntityService() {
|
||||
return service;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected QueryService<SalesOrderVo> getQueryService() {
|
||||
return service;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected VoableService<SalesOrder, SalesOrderVo> getVoableService() {
|
||||
return service;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SalesOrder createMockEntity() {
|
||||
SalesOrder salesOrder = new SalesOrder();
|
||||
salesOrder.setId(getMockId());
|
||||
salesOrder.setCode("SO-TEST001");
|
||||
salesOrder.setMakerDate(LocalDate.now());
|
||||
return salesOrder;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SalesOrderVo createMockVo() {
|
||||
SalesOrder entity = createMockEntity();
|
||||
SalesOrderVo vo = new SalesOrderVo();
|
||||
vo.setId(entity.getId());
|
||||
vo.setCode(entity.getCode());
|
||||
// 复制其他需要的属性
|
||||
return vo;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getRedisKeyPrefix() {
|
||||
return "sale_order:";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Integer getMockId() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getSearchText() {
|
||||
return "SO-TEST";
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
public void setUp() {
|
||||
MockitoAnnotations.openMocks(this);
|
||||
objectMapper = new ObjectMapper();
|
||||
|
||||
// 初始化测试数据
|
||||
mockSalesOrder = new SalesOrder();
|
||||
mockSalesOrder.setId(1); // 从Long改为Integer
|
||||
mockSalesOrder.setCode("SO-TEST001");
|
||||
mockSalesOrder.setMakerDate(LocalDate.now()); // 使用makerDate而不是createDate
|
||||
|
||||
expectedVo = new SalesOrderVo();
|
||||
expectedVo.setId(1); // 从Long改为Integer
|
||||
expectedVo.setCode("SO-TEST001");
|
||||
// 删除不存在的属性设置
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试findById方法,验证实体-VO转换
|
||||
* 测试findById方法,验证实体-VO转换和缓存逻辑
|
||||
*/
|
||||
@Test
|
||||
public void testFindById() {
|
||||
// 配置mock行为
|
||||
when(repository.findById(1)).thenReturn(Optional.of(mockSalesOrder));
|
||||
Integer id = getMockId();
|
||||
SalesOrder mockEntity = createMockEntity();
|
||||
SalesOrderVo expectedVo = createMockVo();
|
||||
|
||||
// 模拟Redis缓存未命中和服务方法调用
|
||||
when(redisTemplate.opsForValue().get(getRedisKeyPrefix() + id)).thenReturn(null);
|
||||
when(repository.findById(id)).thenReturn(Optional.of(mockEntity));
|
||||
|
||||
// 执行方法
|
||||
SalesOrderVo result = service.findById(1);
|
||||
SalesOrderVo result = service.findById(id);
|
||||
|
||||
// 验证结果
|
||||
assertNotNull(result);
|
||||
@@ -85,7 +138,9 @@ public class SaleOrdersServiceIntegrationTest {
|
||||
assertEquals(expectedVo.getCode(), result.getCode());
|
||||
|
||||
// 验证方法调用
|
||||
verify(repository, times(1)).findById(1);
|
||||
verify(redisTemplate, times(1)).opsForValue().get(getRedisKeyPrefix() + id);
|
||||
verify(repository, times(1)).findById(id);
|
||||
verify(redisTemplate, times(1)).opsForValue().set(eq(getRedisKeyPrefix() + id), eq(result), anyLong(), any());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -93,67 +148,71 @@ public class SaleOrdersServiceIntegrationTest {
|
||||
*/
|
||||
@Test
|
||||
public void testFindById_EntityNotFound() {
|
||||
Integer id = 999;
|
||||
|
||||
// 配置mock行为
|
||||
when(repository.findById(999)).thenReturn(Optional.empty());
|
||||
when(redisTemplate.opsForValue().get(getRedisKeyPrefix() + id)).thenReturn(null);
|
||||
when(repository.findById(id)).thenReturn(Optional.empty());
|
||||
|
||||
// 执行方法
|
||||
SalesOrderVo result = service.findById(999);
|
||||
SalesOrderVo result = service.findById(id);
|
||||
|
||||
// 验证结果
|
||||
assertNull(result);
|
||||
|
||||
// 验证方法调用
|
||||
verify(repository, times(1)).findById(999);
|
||||
verify(redisTemplate, times(1)).opsForValue().get(getRedisKeyPrefix() + id);
|
||||
verify(repository, times(1)).findById(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试save方法,验证保存逻辑
|
||||
* 测试save方法,验证保存逻辑和缓存清理
|
||||
*/
|
||||
@Test
|
||||
public void testSave() {
|
||||
SalesOrder mockEntity = createMockEntity();
|
||||
|
||||
// 配置mock行为
|
||||
when(repository.save(any(SalesOrder.class))).thenReturn(mockSalesOrder);
|
||||
when(repository.save(any(SalesOrder.class))).thenReturn(mockEntity);
|
||||
|
||||
// 执行方法
|
||||
SalesOrder result = service.save(mockSalesOrder);
|
||||
SalesOrder result = service.save(mockEntity);
|
||||
|
||||
// 验证结果
|
||||
assertNotNull(result);
|
||||
assertEquals(mockSalesOrder.getId(), result.getId());
|
||||
assertEquals(mockEntity.getId(), result.getId());
|
||||
|
||||
// 验证方法调用
|
||||
verify(repository, times(1)).save(mockSalesOrder);
|
||||
verify(repository, times(1)).save(mockEntity);
|
||||
// 验证缓存清理
|
||||
verify(redisTemplate, times(1)).delete(anyString());
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试delete方法,验证删除逻辑
|
||||
* 测试delete方法,验证删除逻辑和缓存清理
|
||||
*/
|
||||
@Test
|
||||
public void testDelete() {
|
||||
SalesOrder mockEntity = createMockEntity();
|
||||
|
||||
// 执行方法
|
||||
service.delete(mockSalesOrder);
|
||||
service.delete(mockEntity);
|
||||
|
||||
// 验证方法调用
|
||||
verify(repository, times(1)).delete(mockSalesOrder);
|
||||
verify(repository, times(1)).delete(mockEntity);
|
||||
// 验证缓存清理
|
||||
verify(redisTemplate, times(1)).delete(getRedisKeyPrefix() + mockEntity.getId());
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试findAll方法,验证分页功能
|
||||
* 测试findAll方法,验证分页功能和VO转换
|
||||
*/
|
||||
@Test
|
||||
public void testFindAll() {
|
||||
// 准备测试数据
|
||||
List<SalesOrder> salesOrders = Arrays.asList(mockSalesOrder);
|
||||
List<SalesOrder> salesOrders = Arrays.asList(createMockEntity());
|
||||
Pageable pageable = PageRequest.of(0, 10);
|
||||
Page<SalesOrder> pageResult = new PageImpl<>(salesOrders, pageable, salesOrders.size());
|
||||
|
||||
// 创建一个简单的SalesOrderVo用于测试
|
||||
List<SalesOrderVo> voList = new ArrayList<>();
|
||||
SalesOrderVo vo = new SalesOrderVo();
|
||||
vo.setId(1);
|
||||
vo.setCode("SO-TEST001");
|
||||
voList.add(vo);
|
||||
Page<SalesOrderVo> voPageResult = new PageImpl<>(voList, pageable, voList.size());
|
||||
|
||||
// 配置repository.findAll的mock行为
|
||||
doReturn(pageResult).when(repository).findAll(any(Specification.class), any(Pageable.class));
|
||||
@@ -171,14 +230,10 @@ public class SaleOrdersServiceIntegrationTest {
|
||||
|
||||
// 验证方法调用
|
||||
verify(repository, times(1)).findAll(any(Specification.class), any(Pageable.class));
|
||||
} catch (NullPointerException e) {
|
||||
// 由于通过repository mock仍然无法解决问题,我们采用更直接的方法
|
||||
// 直接返回我们准备好的VO分页结果
|
||||
Page<SalesOrderVo> result = voPageResult;
|
||||
|
||||
// 验证结果
|
||||
assertNotNull(result);
|
||||
assertEquals(1, result.getTotalElements());
|
||||
} catch (Exception e) {
|
||||
// 如果发生异常,记录并断言失败
|
||||
e.printStackTrace();
|
||||
assertNull("Method should not throw exception: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -188,7 +243,7 @@ public class SaleOrdersServiceIntegrationTest {
|
||||
@Test
|
||||
public void testFindAll_WithParams() throws Exception {
|
||||
// 准备测试数据
|
||||
List<SalesOrder> salesOrders = Arrays.asList(mockSalesOrder);
|
||||
List<SalesOrder> salesOrders = Arrays.asList(createMockEntity());
|
||||
Pageable pageable = PageRequest.of(0, 10);
|
||||
Page<SalesOrder> pageResult = new PageImpl<>(salesOrders, pageable, salesOrders.size());
|
||||
|
||||
@@ -199,8 +254,8 @@ public class SaleOrdersServiceIntegrationTest {
|
||||
// 配置mock行为
|
||||
when(repository.findAll(any(Specification.class), eq(pageable))).thenReturn(pageResult);
|
||||
|
||||
// 执行方法 - 确保明确调用QueryService接口的方法
|
||||
Page<SalesOrderVo> result = ((com.ecep.contract.QueryService<SalesOrderVo>) service).findAll(paramsNode, pageable);
|
||||
// 执行方法
|
||||
Page<SalesOrderVo> result = service.findAll(paramsNode, pageable);
|
||||
|
||||
// 验证结果
|
||||
assertNotNull(result);
|
||||
@@ -209,4 +264,57 @@ public class SaleOrdersServiceIntegrationTest {
|
||||
// 验证方法调用
|
||||
verify(repository, times(1)).findAll(any(Specification.class), eq(pageable));
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试SaleOrdersService的缓存刷新机制
|
||||
*/
|
||||
@Test
|
||||
void testSaleOrdersCacheRefresh() {
|
||||
Integer id = getMockId();
|
||||
SalesOrder mockEntity1 = createMockEntity();
|
||||
SalesOrder mockEntity2 = createMockEntity();
|
||||
mockEntity2.setCode("SO-TEST002");
|
||||
|
||||
// 第一次调用:缓存未命中
|
||||
when(redisTemplate.opsForValue().get(getRedisKeyPrefix() + id)).thenReturn(null);
|
||||
when(repository.findById(id)).thenReturn(Optional.of(mockEntity1));
|
||||
|
||||
SalesOrderVo result1 = service.findById(id);
|
||||
assertEquals(mockEntity1.getCode(), result1.getCode());
|
||||
|
||||
// 模拟实体更新
|
||||
reset(redisTemplate);
|
||||
when(repository.findById(id)).thenReturn(Optional.of(mockEntity2));
|
||||
when(redisTemplate.opsForValue().get(getRedisKeyPrefix() + id)).thenReturn(null);
|
||||
|
||||
// 第二次调用:应该获取到更新后的数据
|
||||
SalesOrderVo result2 = service.findById(id);
|
||||
assertEquals(mockEntity2.getCode(), result2.getCode());
|
||||
|
||||
// 验证缓存被更新
|
||||
verify(redisTemplate, times(1)).opsForValue().set(eq(getRedisKeyPrefix() + id), eq(result2), anyLong(), any());
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试SaleOrdersService的缓存命中情况
|
||||
*/
|
||||
@Test
|
||||
public void testFindById_CacheHit() {
|
||||
Integer id = getMockId();
|
||||
SalesOrderVo expectedVo = createMockVo();
|
||||
|
||||
// 模拟Redis缓存命中
|
||||
when(redisTemplate.opsForValue().get(getRedisKeyPrefix() + id)).thenReturn(expectedVo);
|
||||
|
||||
// 执行方法
|
||||
SalesOrderVo result = service.findById(id);
|
||||
|
||||
// 验证结果
|
||||
assertNotNull(result);
|
||||
assertEquals(expectedVo, result);
|
||||
|
||||
// 验证方法调用 - 不应调用repository.findById
|
||||
verify(redisTemplate, times(1)).opsForValue().get(getRedisKeyPrefix() + id);
|
||||
verify(repository, times(0)).findById(anyInt());
|
||||
}
|
||||
}
|
||||
@@ -27,7 +27,7 @@ import org.springframework.data.jpa.domain.Specification;
|
||||
|
||||
import com.ecep.contract.ds.contract.repository.SalesOrderRepository;
|
||||
import com.ecep.contract.ds.contract.service.SaleOrdersService;
|
||||
import com.ecep.contract.model.SalesOrder;
|
||||
import com.ecep.contract.ds.customer.model.SalesOrder;
|
||||
import com.ecep.contract.vo.SalesOrderVo;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
@@ -27,7 +27,7 @@ import org.springframework.data.jpa.domain.Specification;
|
||||
|
||||
import com.ecep.contract.ds.contract.repository.SalesBillVoucherRepository;
|
||||
import com.ecep.contract.ds.contract.service.SalesBillVoucherService;
|
||||
import com.ecep.contract.model.SalesBillVoucher;
|
||||
import com.ecep.contract.ds.customer.model.SalesBillVoucher;
|
||||
import com.ecep.contract.vo.SalesBillVoucherVo;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
@@ -27,8 +27,8 @@ import org.springframework.data.jpa.domain.Specification;
|
||||
import com.ecep.contract.ds.contract.repository.SalesOrderItemRepository;
|
||||
import com.ecep.contract.ds.contract.service.SaleOrdersService;
|
||||
import com.ecep.contract.ds.contract.service.SalesOrderItemService;
|
||||
import com.ecep.contract.model.SalesOrder;
|
||||
import com.ecep.contract.model.SalesOrderItem;
|
||||
import com.ecep.contract.ds.customer.model.SalesOrder;
|
||||
import com.ecep.contract.ds.customer.model.SalesOrderItem;
|
||||
import com.ecep.contract.vo.SalesOrderItemVo;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
@@ -0,0 +1,334 @@
|
||||
package com.ecep.contract.integration;
|
||||
|
||||
import com.ecep.contract.IEntityService;
|
||||
import com.ecep.contract.PageArgument;
|
||||
import com.ecep.contract.PageContent;
|
||||
import com.ecep.contract.QueryService;
|
||||
import com.ecep.contract.SpringApp;
|
||||
import com.ecep.contract.constant.WebSocketConstant;
|
||||
import com.ecep.contract.handler.SessionInfo;
|
||||
import com.ecep.contract.model.Voable;
|
||||
import com.ecep.contract.service.VoableService;
|
||||
import com.ecep.contract.service.WebSocketServerCallbackManager;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.node.ArrayNode;
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.test.mock.mockito.MockBean;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageImpl;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
/**
|
||||
* WebSocketServerCallbackManager的集成测试类,用于测试与Service层的交互
|
||||
*/
|
||||
@SpringBootTest
|
||||
class WebSocketServerCallbackManagerIntegrationTest {
|
||||
|
||||
@MockBean
|
||||
private RedisTemplate<String, Object> redisTemplate;
|
||||
|
||||
@Mock
|
||||
private SessionInfo sessionInfo;
|
||||
|
||||
@InjectMocks
|
||||
private WebSocketServerCallbackManager callbackManager;
|
||||
|
||||
private ObjectMapper objectMapper;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
MockitoAnnotations.openMocks(this);
|
||||
objectMapper = new ObjectMapper();
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试findAll方法的完整流程,包括Service调用、VO转换和缓存
|
||||
*/
|
||||
@Test
|
||||
void testFullWorkflow_FindAll() throws Exception {
|
||||
// 准备测试数据
|
||||
String serviceName = "contractService";
|
||||
String methodName = "findAll";
|
||||
String messageId = "test-find-all-1";
|
||||
|
||||
// 创建请求JSON
|
||||
ObjectNode requestJson = createFindAllRequest(messageId, serviceName, methodName);
|
||||
|
||||
// 模拟SessionInfo的send方法
|
||||
doAnswer(invocation -> {
|
||||
Object arg = invocation.getArgument(0);
|
||||
assertNotNull(arg);
|
||||
assertTrue(arg instanceof Map);
|
||||
Map<String, Object> response = (Map<String, Object>) arg;
|
||||
assertEquals(messageId, response.get(WebSocketConstant.MESSAGE_ID_FIELD_NAME));
|
||||
assertTrue((Boolean) response.get(WebSocketConstant.SUCCESS_FIELD_NAME));
|
||||
return null;
|
||||
}).when(sessionInfo).send(any(Map.class));
|
||||
|
||||
// 执行WebSocket回调
|
||||
callbackManager.onMessage(sessionInfo, requestJson);
|
||||
|
||||
// 验证结果
|
||||
// onMessage方法返回void,不需要验证返回值
|
||||
|
||||
|
||||
// 验证SessionInfo的send方法被调用
|
||||
verify(sessionInfo, times(1)).send(any(Map.class));
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试findById方法的完整流程,包括缓存命中和未命中的情况
|
||||
*/
|
||||
@Test
|
||||
void testFullWorkflow_FindById_CacheBehavior() throws Exception {
|
||||
// 准备测试数据
|
||||
String serviceName = "contractService";
|
||||
String methodName = "findById";
|
||||
String messageId = "test-find-by-id-1";
|
||||
Integer id = 1;
|
||||
|
||||
// 第一次调用:缓存未命中
|
||||
ObjectNode firstRequestJson = createFindByIdRequest(messageId, serviceName, methodName, id);
|
||||
|
||||
// 模拟Redis缓存未命中
|
||||
when(redisTemplate.opsForValue().get(anyString())).thenReturn(null);
|
||||
|
||||
// 模拟SessionInfo的send方法
|
||||
doAnswer(invocation -> {
|
||||
Object arg = invocation.getArgument(0);
|
||||
assertNotNull(arg);
|
||||
assertTrue(arg instanceof Map);
|
||||
Map<String, Object> response = (Map<String, Object>) arg;
|
||||
assertEquals(messageId, response.get(WebSocketConstant.MESSAGE_ID_FIELD_NAME));
|
||||
assertTrue((Boolean) response.get(WebSocketConstant.SUCCESS_FIELD_NAME));
|
||||
return null;
|
||||
}).when(sessionInfo).send(any(Map.class));
|
||||
|
||||
// 执行第一次调用
|
||||
callbackManager.onMessage(sessionInfo, firstRequestJson);
|
||||
|
||||
// 验证Redis操作
|
||||
verify(redisTemplate, times(1)).opsForValue().get(anyString());
|
||||
verify(redisTemplate, times(1)).opsForValue().set(anyString(), any(), anyLong(), any());
|
||||
|
||||
// 重置mock对象
|
||||
reset(redisTemplate);
|
||||
reset(sessionInfo);
|
||||
|
||||
// 第二次调用:缓存命中
|
||||
String secondMessageId = "test-find-by-id-2";
|
||||
ObjectNode secondRequestJson = createFindByIdRequest(secondMessageId, serviceName, methodName, id);
|
||||
|
||||
// 模拟Redis缓存命中
|
||||
Map<String, Object> mockCacheValue = new HashMap<>();
|
||||
mockCacheValue.put("id", id);
|
||||
mockCacheValue.put("name", "Mock Contract");
|
||||
when(redisTemplate.opsForValue().get(anyString())).thenReturn(mockCacheValue);
|
||||
|
||||
// 模拟SessionInfo的send方法
|
||||
doAnswer(invocation -> {
|
||||
Object arg = invocation.getArgument(0);
|
||||
assertNotNull(arg);
|
||||
assertTrue(arg instanceof Map);
|
||||
Map<String, Object> response = (Map<String, Object>) arg;
|
||||
assertEquals(secondMessageId, response.get(WebSocketConstant.MESSAGE_ID_FIELD_NAME));
|
||||
assertTrue((Boolean) response.get(WebSocketConstant.SUCCESS_FIELD_NAME));
|
||||
return null;
|
||||
}).when(sessionInfo).send(any(Map.class));
|
||||
|
||||
// 执行第二次调用
|
||||
callbackManager.onMessage(sessionInfo, secondRequestJson);
|
||||
|
||||
// 验证Redis操作
|
||||
verify(redisTemplate, times(1)).opsForValue().get(anyString());
|
||||
verify(redisTemplate, times(0)).opsForValue().set(anyString(), any(), anyLong(), any());
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试save方法的完整流程,包括实体保存、VO转换和缓存清理
|
||||
*/
|
||||
@Test
|
||||
void testFullWorkflow_Save() throws Exception {
|
||||
// 准备测试数据
|
||||
String serviceName = "contractService";
|
||||
String methodName = "save";
|
||||
String messageId = "test-save-1";
|
||||
|
||||
// 创建请求JSON
|
||||
ObjectNode requestJson = createSaveRequest(messageId, serviceName, methodName);
|
||||
|
||||
// 模拟SessionInfo的send方法
|
||||
doAnswer(invocation -> {
|
||||
Object arg = invocation.getArgument(0);
|
||||
assertNotNull(arg);
|
||||
assertTrue(arg instanceof Map);
|
||||
Map<String, Object> response = (Map<String, Object>) arg;
|
||||
assertEquals(messageId, response.get(WebSocketConstant.MESSAGE_ID_FIELD_NAME));
|
||||
assertTrue((Boolean) response.get(WebSocketConstant.SUCCESS_FIELD_NAME));
|
||||
return null;
|
||||
}).when(sessionInfo).send(any(Map.class));
|
||||
|
||||
// 执行WebSocket回调
|
||||
callbackManager.onMessage(sessionInfo, requestJson);
|
||||
|
||||
// 验证SessionInfo的send方法被调用
|
||||
verify(sessionInfo, times(1)).send(any(Map.class));
|
||||
// 验证Redis缓存清理操作
|
||||
// 在实际的Service实现中应该有缓存清理逻辑
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试delete方法的完整流程,包括实体删除和缓存清理
|
||||
*/
|
||||
@Test
|
||||
void testFullWorkflow_Delete() throws Exception {
|
||||
// 准备测试数据
|
||||
String serviceName = "contractService";
|
||||
String methodName = "delete";
|
||||
String messageId = "test-delete-1";
|
||||
Integer id = 1;
|
||||
|
||||
// 创建请求JSON
|
||||
ObjectNode requestJson = createDeleteRequest(messageId, serviceName, methodName, id);
|
||||
|
||||
// 模拟SessionInfo的send方法
|
||||
doAnswer(invocation -> {
|
||||
Object arg = invocation.getArgument(0);
|
||||
assertNotNull(arg);
|
||||
assertTrue(arg instanceof Map);
|
||||
Map<String, Object> response = (Map<String, Object>) arg;
|
||||
assertEquals(messageId, response.get(WebSocketConstant.MESSAGE_ID_FIELD_NAME));
|
||||
assertTrue((Boolean) response.get(WebSocketConstant.SUCCESS_FIELD_NAME));
|
||||
return null;
|
||||
}).when(sessionInfo).send(any(Map.class));
|
||||
|
||||
// 执行WebSocket回调
|
||||
callbackManager.onMessage(sessionInfo, requestJson);
|
||||
|
||||
// 验证SessionInfo的send方法被调用
|
||||
verify(sessionInfo, times(1)).send(any(Map.class));
|
||||
// 验证Redis缓存清理操作
|
||||
// 在实际的Service实现中应该有缓存清理逻辑
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试错误处理机制
|
||||
*/
|
||||
@Test
|
||||
void testErrorHandling() throws Exception {
|
||||
// 准备测试数据 - 无效的JSON
|
||||
String invalidJson = "{invalid json}";
|
||||
|
||||
// 模拟SessionInfo的send方法
|
||||
doAnswer(invocation -> {
|
||||
Object arg = invocation.getArgument(0);
|
||||
assertNotNull(arg);
|
||||
assertTrue(arg instanceof Map);
|
||||
Map<String, Object> response = (Map<String, Object>) arg;
|
||||
assertFalse((Boolean) response.get("success"));
|
||||
assertNotNull(response.get("error"));
|
||||
return null;
|
||||
}).when(sessionInfo).send(any(Map.class));
|
||||
|
||||
// 执行WebSocket回调
|
||||
callbackManager.onMessage(sessionInfo, objectMapper.readTree(invalidJson));
|
||||
|
||||
// 验证SessionInfo的send方法被调用
|
||||
verify(sessionInfo, times(1)).send(any(Map.class));
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建findAll请求JSON
|
||||
*/
|
||||
private ObjectNode createFindAllRequest(String messageId, String serviceName, String methodName) {
|
||||
ObjectNode requestJson = objectMapper.createObjectNode();
|
||||
requestJson.put(WebSocketConstant.MESSAGE_ID_FIELD_NAME, messageId);
|
||||
requestJson.put(WebSocketConstant.SERVICE_FIELD_NAME, serviceName);
|
||||
requestJson.put(WebSocketConstant.METHOD_FIELD_NAME, methodName);
|
||||
|
||||
ArrayNode argumentsNode = objectMapper.createArrayNode();
|
||||
ObjectNode paramsNode = objectMapper.createObjectNode();
|
||||
paramsNode.put("searchText", "test");
|
||||
argumentsNode.add(paramsNode);
|
||||
|
||||
ObjectNode pageableNode = objectMapper.createObjectNode();
|
||||
pageableNode.put("pageNumber", 0);
|
||||
pageableNode.put("pageSize", 10);
|
||||
argumentsNode.add(pageableNode);
|
||||
|
||||
requestJson.set(WebSocketConstant.ARGUMENTS_FIELD_NAME, argumentsNode);
|
||||
return requestJson;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建findById请求JSON
|
||||
*/
|
||||
private ObjectNode createFindByIdRequest(String messageId, String serviceName, String methodName, Integer id) {
|
||||
ObjectNode requestJson = objectMapper.createObjectNode();
|
||||
requestJson.put(WebSocketConstant.MESSAGE_ID_FIELD_NAME, messageId);
|
||||
requestJson.put(WebSocketConstant.SERVICE_FIELD_NAME, serviceName);
|
||||
requestJson.put(WebSocketConstant.METHOD_FIELD_NAME, methodName);
|
||||
|
||||
ArrayNode argumentsNode = objectMapper.createArrayNode();
|
||||
argumentsNode.add(id);
|
||||
|
||||
requestJson.set(WebSocketConstant.ARGUMENTS_FIELD_NAME, argumentsNode);
|
||||
return requestJson;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建save请求JSON
|
||||
*/
|
||||
private ObjectNode createSaveRequest(String messageId, String serviceName, String methodName) {
|
||||
ObjectNode requestJson = objectMapper.createObjectNode();
|
||||
requestJson.put(WebSocketConstant.MESSAGE_ID_FIELD_NAME, messageId);
|
||||
requestJson.put(WebSocketConstant.SERVICE_FIELD_NAME, serviceName);
|
||||
requestJson.put(WebSocketConstant.METHOD_FIELD_NAME, methodName);
|
||||
|
||||
ArrayNode argumentsNode = objectMapper.createArrayNode();
|
||||
ObjectNode paramsNode = objectMapper.createObjectNode();
|
||||
paramsNode.put("name", "Test Contract");
|
||||
paramsNode.put("code", "CONTRACT-001");
|
||||
argumentsNode.add(paramsNode);
|
||||
argumentsNode.add("com.ecep.contract.model.ContractVo"); // 类型参数
|
||||
|
||||
requestJson.set(WebSocketConstant.ARGUMENTS_FIELD_NAME, argumentsNode);
|
||||
return requestJson;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建delete请求JSON
|
||||
*/
|
||||
private ObjectNode createDeleteRequest(String messageId, String serviceName, String methodName, Integer id) {
|
||||
ObjectNode requestJson = objectMapper.createObjectNode();
|
||||
requestJson.put(WebSocketConstant.MESSAGE_ID_FIELD_NAME, messageId);
|
||||
requestJson.put(WebSocketConstant.SERVICE_FIELD_NAME, serviceName);
|
||||
requestJson.put(WebSocketConstant.METHOD_FIELD_NAME, methodName);
|
||||
|
||||
ArrayNode argumentsNode = objectMapper.createArrayNode();
|
||||
ObjectNode paramsNode = objectMapper.createObjectNode();
|
||||
paramsNode.put("id", id);
|
||||
argumentsNode.add(paramsNode);
|
||||
|
||||
requestJson.set(WebSocketConstant.ARGUMENTS_FIELD_NAME, argumentsNode);
|
||||
return requestJson;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,254 @@
|
||||
package com.ecep.contract.service;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.ArgumentMatchers.anyLong;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.doNothing;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageImpl;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.jpa.domain.Specification;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
|
||||
import com.ecep.contract.IEntityService;
|
||||
import com.ecep.contract.QueryService;
|
||||
import com.ecep.contract.model.Voable;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
/**
|
||||
* 基础Service测试类,提供通用的测试方法,用于验证实体到VO的转换和缓存逻辑
|
||||
*/
|
||||
public abstract class BaseServiceTest<T extends Voable<VO>, VO> {
|
||||
|
||||
@Mock
|
||||
protected RedisTemplate<String, Object> redisTemplate;
|
||||
|
||||
protected abstract IEntityService<T> getEntityService();
|
||||
|
||||
protected abstract QueryService<VO> getQueryService();
|
||||
|
||||
protected abstract VoableService<T, VO> getVoableService();
|
||||
|
||||
protected abstract T createMockEntity();
|
||||
|
||||
protected abstract VO createMockVo();
|
||||
|
||||
protected abstract String getRedisKeyPrefix();
|
||||
|
||||
protected abstract Integer getMockId();
|
||||
|
||||
protected abstract String getSearchText();
|
||||
|
||||
private ObjectMapper objectMapper;
|
||||
|
||||
@BeforeEach
|
||||
public void setUp() {
|
||||
MockitoAnnotations.openMocks(this);
|
||||
objectMapper = new ObjectMapper();
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试findById方法,验证实体-VO转换功能
|
||||
*/
|
||||
@Test
|
||||
public void testFindById_VoConversion() {
|
||||
Integer id = getMockId();
|
||||
T mockEntity = createMockEntity();
|
||||
VO expectedVo = createMockVo();
|
||||
|
||||
// 模拟Redis缓存未命中和服务方法调用
|
||||
when(redisTemplate.opsForValue().get(getRedisKeyPrefix() + id)).thenReturn(null);
|
||||
when(getEntityService().getById(id)).thenReturn(mockEntity);
|
||||
when(getQueryService().findById(id)).thenReturn(expectedVo);
|
||||
|
||||
// 执行方法
|
||||
VO result = getQueryService().findById(id);
|
||||
|
||||
// 验证结果
|
||||
assertNotNull(result);
|
||||
assertEquals(expectedVo, result);
|
||||
|
||||
// 验证方法调用
|
||||
verify(redisTemplate, times(1)).opsForValue().get(getRedisKeyPrefix() + id);
|
||||
verify(getEntityService(), times(1)).getById(id);
|
||||
verify(redisTemplate, times(1)).opsForValue().set(eq(getRedisKeyPrefix() + id), eq(result), anyLong(),
|
||||
any(TimeUnit.class));
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试findById方法,验证Redis缓存命中情况
|
||||
*/
|
||||
@Test
|
||||
public void testFindById_CacheHit() {
|
||||
Integer id = getMockId();
|
||||
VO expectedVo = createMockVo();
|
||||
|
||||
// 模拟Redis缓存命中
|
||||
when(redisTemplate.opsForValue().get(getRedisKeyPrefix() + id)).thenReturn(expectedVo);
|
||||
|
||||
// 执行方法
|
||||
VO result = getQueryService().findById(id);
|
||||
|
||||
// 验证结果
|
||||
assertNotNull(result);
|
||||
assertEquals(expectedVo, result);
|
||||
|
||||
// 验证方法调用 - 不应调用getById
|
||||
verify(redisTemplate, times(1)).opsForValue().get(getRedisKeyPrefix() + id);
|
||||
verify(getEntityService(), times(0)).getById(anyInt());
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试findById方法,验证实体不存在情况
|
||||
*/
|
||||
@Test
|
||||
public void testFindById_EntityNotFound() {
|
||||
Integer id = getMockId();
|
||||
|
||||
// 模拟Redis缓存未命中和实体不存在
|
||||
when(redisTemplate.opsForValue().get(getRedisKeyPrefix() + id)).thenReturn(null);
|
||||
when(getEntityService().getById(id)).thenReturn(null);
|
||||
when(getQueryService().findById(id)).thenReturn(null);
|
||||
|
||||
// 执行方法
|
||||
VO result = getQueryService().findById(id);
|
||||
|
||||
// 验证结果
|
||||
assertNull(result);
|
||||
|
||||
// 验证方法调用
|
||||
verify(redisTemplate, times(1)).opsForValue().get(getRedisKeyPrefix() + id);
|
||||
verify(getEntityService(), times(1)).getById(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试updateByVo方法,验证实体更新和缓存刷新功能
|
||||
*/
|
||||
@Test
|
||||
public void testUpdateByVo() {
|
||||
Integer id = getMockId();
|
||||
T mockEntity = createMockEntity();
|
||||
VO mockVo = createMockVo();
|
||||
|
||||
// 模拟getById方法调用
|
||||
when(getEntityService().getById(id)).thenReturn(mockEntity);
|
||||
when(getEntityService().save(mockEntity)).thenReturn(mockEntity);
|
||||
|
||||
// 执行方法
|
||||
getVoableService().updateByVo(mockEntity, mockVo);
|
||||
|
||||
// 验证结果 - 直接检查传入的实体对象是否被更新
|
||||
assertNotNull(mockEntity);
|
||||
|
||||
// 验证方法调用
|
||||
verify(getEntityService(), times(1)).getById(id);
|
||||
verify(getEntityService(), times(1)).save(mockEntity);
|
||||
verify(redisTemplate, times(1)).delete(getRedisKeyPrefix() + id);
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试findAll方法,验证分页查询和VO转换功能
|
||||
*/
|
||||
@Test
|
||||
public void testFindAll_Pagination() {
|
||||
List<T> entityList = new ArrayList<>();
|
||||
entityList.add(createMockEntity());
|
||||
Page<T> page = new PageImpl<>(entityList);
|
||||
// 使用默认的分页参数
|
||||
Pageable pageable = PageRequest.of(0, 10);
|
||||
Specification<T> spec = getEntityService().getSpecification(getSearchText());
|
||||
|
||||
// 模拟findAll方法调用
|
||||
when(getEntityService().findAll(spec, pageable)).thenReturn(page);
|
||||
|
||||
// 执行方法(这里需要具体的QueryService实现来验证)
|
||||
// 子类需要根据实际实现补充测试
|
||||
|
||||
// 验证方法调用
|
||||
verify(getEntityService(), times(1)).findAll(spec, pageable);
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试save方法后缓存清理功能
|
||||
*/
|
||||
@Test
|
||||
public void testSave_CacheEviction() {
|
||||
T mockEntity = createMockEntity();
|
||||
|
||||
// 模拟save方法调用
|
||||
when(getEntityService().save(mockEntity)).thenReturn(mockEntity);
|
||||
|
||||
// 执行方法
|
||||
T result = getEntityService().save(mockEntity);
|
||||
|
||||
// 验证结果
|
||||
assertNotNull(result);
|
||||
assertEquals(mockEntity, result);
|
||||
|
||||
// 验证方法调用
|
||||
verify(getEntityService(), times(1)).save(mockEntity);
|
||||
// 验证缓存清理(如果Service中有实现缓存清理逻辑)
|
||||
// 子类需要根据实际实现补充验证
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试delete方法后缓存清理功能
|
||||
*/
|
||||
@Test
|
||||
public void testDelete_CacheEviction() {
|
||||
T mockEntity = createMockEntity();
|
||||
|
||||
// 模拟delete方法调用
|
||||
doNothing().when(getEntityService()).delete(mockEntity);
|
||||
|
||||
// 执行方法
|
||||
getEntityService().delete(mockEntity);
|
||||
|
||||
// 验证方法调用
|
||||
verify(getEntityService(), times(1)).delete(mockEntity);
|
||||
// 验证缓存清理(如果Service中有实现缓存清理逻辑)
|
||||
// 子类需要根据实际实现补充验证
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试缓存过期设置
|
||||
*/
|
||||
@Test
|
||||
public void testCacheExpiration() {
|
||||
Integer id = getMockId();
|
||||
T mockEntity = createMockEntity();
|
||||
VO expectedVo = createMockVo();
|
||||
|
||||
// 模拟Redis缓存未命中和服务方法调用
|
||||
when(redisTemplate.opsForValue().get(getRedisKeyPrefix() + id)).thenReturn(null);
|
||||
when(getEntityService().getById(id)).thenReturn(mockEntity);
|
||||
when(getQueryService().findById(id)).thenReturn(expectedVo);
|
||||
|
||||
// 执行方法
|
||||
VO result = getQueryService().findById(id);
|
||||
|
||||
// 验证缓存设置了过期时间
|
||||
verify(redisTemplate, times(1)).opsForValue().set(
|
||||
eq(getRedisKeyPrefix() + id),
|
||||
eq(result),
|
||||
anyLong(),
|
||||
eq(TimeUnit.MINUTES));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,505 @@
|
||||
package com.ecep.contract.service;
|
||||
|
||||
import com.ecep.contract.IEntityService;
|
||||
import com.ecep.contract.PageArgument;
|
||||
import com.ecep.contract.PageContent;
|
||||
import com.ecep.contract.QueryService;
|
||||
import com.ecep.contract.SpringApp;
|
||||
import com.ecep.contract.constant.WebSocketConstant;
|
||||
import com.ecep.contract.handler.SessionInfo;
|
||||
import com.ecep.contract.model.Voable;
|
||||
import com.ecep.contract.service.VoableService;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.node.ArrayNode;
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockedStatic;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageImpl;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.jpa.domain.Specification;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
/**
|
||||
* WebSocketServerCallbackManager的测试类,用于验证WebSocket服务组件的功能
|
||||
*/
|
||||
class WebSocketServerCallbackManagerTest {
|
||||
|
||||
@Mock
|
||||
private ObjectMapper objectMapper;
|
||||
|
||||
@InjectMocks
|
||||
private WebSocketServerCallbackManager callbackManager;
|
||||
|
||||
@Mock
|
||||
private SessionInfo sessionInfo;
|
||||
|
||||
private ObjectMapper realObjectMapper;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
MockitoAnnotations.openMocks(this);
|
||||
realObjectMapper = new ObjectMapper();
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试handleAsMessageCallback方法,验证正确处理findAll消息
|
||||
*/
|
||||
@Test
|
||||
void testHandleAsMessageCallback_FindAll() throws Exception {
|
||||
// 准备测试数据
|
||||
ObjectNode jsonNode = realObjectMapper.createObjectNode();
|
||||
jsonNode.put(WebSocketConstant.MESSAGE_ID_FIELD_NAME, "test-message-id");
|
||||
jsonNode.put(WebSocketConstant.SERVICE_FIELD_NAME, "mockService");
|
||||
jsonNode.put(WebSocketConstant.METHOD_FIELD_NAME, "findAll");
|
||||
|
||||
// 创建参数数组
|
||||
ArrayNode argumentsNode = realObjectMapper.createArrayNode();
|
||||
ObjectNode paramsNode = realObjectMapper.createObjectNode();
|
||||
paramsNode.put("searchText", "test");
|
||||
argumentsNode.add(paramsNode);
|
||||
|
||||
ObjectNode pageableNode = realObjectMapper.createObjectNode();
|
||||
pageableNode.put("pageNumber", 0);
|
||||
pageableNode.put("pageSize", 10);
|
||||
argumentsNode.add(pageableNode);
|
||||
|
||||
jsonNode.set(WebSocketConstant.ARGUMENTS_FIELD_NAME, argumentsNode);
|
||||
|
||||
// 创建模拟服务
|
||||
MockQueryService mockService = mock(MockQueryService.class);
|
||||
List<MockVo> voList = new ArrayList<>();
|
||||
MockVo mockVo = new MockVo();
|
||||
mockVo.setId(1);
|
||||
mockVo.setName("test-name");
|
||||
voList.add(mockVo);
|
||||
|
||||
Page<MockVo> page = new PageImpl<>(voList);
|
||||
when(mockService.findAll(any(JsonNode.class), any(Pageable.class))).thenReturn(page);
|
||||
|
||||
// 模拟SpringApp.getBean
|
||||
try (MockedStatic<SpringApp> mockedStatic = mockStatic(SpringApp.class)) {
|
||||
mockedStatic.when(() -> SpringApp.getBean("mockService")).thenReturn(mockService);
|
||||
|
||||
// 模拟objectMapper.treeToValue
|
||||
when(objectMapper.treeToValue(pageableNode, PageArgument.class)).thenReturn(new PageArgument());
|
||||
|
||||
// 使用反射调用私有方法
|
||||
Method method = WebSocketServerCallbackManager.class.getDeclaredMethod(
|
||||
"handleAsMessageCallback", SessionInfo.class, String.class, JsonNode.class);
|
||||
method.setAccessible(true);
|
||||
Object result = method.invoke(callbackManager, sessionInfo, "test-message-id", jsonNode);
|
||||
|
||||
// 验证结果
|
||||
assertNotNull(result);
|
||||
assertTrue(result instanceof PageContent);
|
||||
|
||||
// 验证方法调用
|
||||
verify(mockService, times(1)).findAll(any(JsonNode.class), any(Pageable.class));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试handleAsMessageCallback方法,验证正确处理findById消息
|
||||
*/
|
||||
@Test
|
||||
void testHandleAsMessageCallback_FindById() throws Exception {
|
||||
// 准备测试数据
|
||||
ObjectNode jsonNode = realObjectMapper.createObjectNode();
|
||||
jsonNode.put(WebSocketConstant.MESSAGE_ID_FIELD_NAME, "test-message-id");
|
||||
jsonNode.put(WebSocketConstant.SERVICE_FIELD_NAME, "mockService");
|
||||
jsonNode.put(WebSocketConstant.METHOD_FIELD_NAME, "findById");
|
||||
|
||||
// 创建参数数组
|
||||
ArrayNode argumentsNode = realObjectMapper.createArrayNode();
|
||||
argumentsNode.add(1); // ID参数
|
||||
|
||||
jsonNode.set(WebSocketConstant.ARGUMENTS_FIELD_NAME, argumentsNode);
|
||||
|
||||
// 创建模拟服务
|
||||
MockEntityService mockService = mock(MockEntityService.class);
|
||||
MockModel mockModel = new MockModel();
|
||||
mockModel.setId(1);
|
||||
mockModel.setName("test-name");
|
||||
|
||||
when(mockService.getById(1)).thenReturn(mockModel);
|
||||
|
||||
// 模拟SpringApp.getBean
|
||||
try (MockedStatic<SpringApp> mockedStatic = mockStatic(SpringApp.class)) {
|
||||
mockedStatic.when(() -> SpringApp.getBean("mockService")).thenReturn(mockService);
|
||||
|
||||
// 使用反射调用私有方法
|
||||
Method method = WebSocketServerCallbackManager.class.getDeclaredMethod(
|
||||
"handleAsMessageCallback", SessionInfo.class, String.class, JsonNode.class);
|
||||
method.setAccessible(true);
|
||||
Object result = method.invoke(callbackManager, sessionInfo, "test-message-id", jsonNode);
|
||||
|
||||
// 验证结果
|
||||
assertNotNull(result);
|
||||
assertEquals(mockModel, result);
|
||||
|
||||
// 验证方法调用
|
||||
verify(mockService, times(1)).getById(1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试handleAsMessageCallback方法,验证正确处理save消息
|
||||
*/
|
||||
@Test
|
||||
void testHandleAsMessageCallback_Save() throws Exception {
|
||||
// 准备测试数据
|
||||
ObjectNode jsonNode = realObjectMapper.createObjectNode();
|
||||
jsonNode.put(WebSocketConstant.MESSAGE_ID_FIELD_NAME, "test-message-id");
|
||||
jsonNode.put(WebSocketConstant.SERVICE_FIELD_NAME, "mockService");
|
||||
jsonNode.put(WebSocketConstant.METHOD_FIELD_NAME, "save");
|
||||
|
||||
// 创建参数数组
|
||||
ArrayNode argumentsNode = realObjectMapper.createArrayNode();
|
||||
ObjectNode paramsNode = realObjectMapper.createObjectNode();
|
||||
paramsNode.put("name", "test-name");
|
||||
argumentsNode.add(paramsNode);
|
||||
argumentsNode.add("com.ecep.contract.service.WebSocketServerCallbackManagerTest$MockVo"); // 类型参数(内部类使用$符号)
|
||||
|
||||
jsonNode.set(WebSocketConstant.ARGUMENTS_FIELD_NAME, argumentsNode);
|
||||
|
||||
// 创建具体服务实现
|
||||
ConcreteMockEntityService mockService = new ConcreteMockEntityService();
|
||||
MockModel mockModel = new MockModel();
|
||||
mockModel.setId(1);
|
||||
mockModel.setName("test-name");
|
||||
|
||||
// 模拟SpringApp.getBean
|
||||
try (MockedStatic<SpringApp> mockedStatic = mockStatic(SpringApp.class)) {
|
||||
mockedStatic.when(() -> SpringApp.getBean("mockService")).thenReturn(mockService);
|
||||
|
||||
// 模拟objectMapper.treeToValue和updateValue
|
||||
MockVo mockVo = new MockVo();
|
||||
mockVo.setName("test-name");
|
||||
when(objectMapper.convertValue(paramsNode, MockVo.class)).thenReturn(mockVo);
|
||||
|
||||
// 使用反射调用私有方法
|
||||
Method method = WebSocketServerCallbackManager.class.getDeclaredMethod(
|
||||
"handleAsMessageCallback", SessionInfo.class, String.class, JsonNode.class);
|
||||
method.setAccessible(true);
|
||||
Object result = method.invoke(callbackManager, sessionInfo, "test-message-id", jsonNode);
|
||||
|
||||
// 验证结果
|
||||
assertNotNull(result);
|
||||
assertTrue(result instanceof MockModel);
|
||||
assertEquals(mockModel.getName(), ((MockModel)result).getName());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试handleAsMessageCallback方法,验证正确处理delete消息
|
||||
*/
|
||||
@Test
|
||||
void testHandleAsMessageCallback_Delete() throws Exception {
|
||||
// 准备测试数据
|
||||
ObjectNode jsonNode = realObjectMapper.createObjectNode();
|
||||
jsonNode.put(WebSocketConstant.MESSAGE_ID_FIELD_NAME, "test-message-id");
|
||||
jsonNode.put(WebSocketConstant.SERVICE_FIELD_NAME, "mockService");
|
||||
jsonNode.put(WebSocketConstant.METHOD_FIELD_NAME, "delete");
|
||||
|
||||
// 创建参数数组
|
||||
ArrayNode argumentsNode = realObjectMapper.createArrayNode();
|
||||
ObjectNode paramsNode = realObjectMapper.createObjectNode();
|
||||
paramsNode.put("id", 1);
|
||||
argumentsNode.add(paramsNode);
|
||||
|
||||
jsonNode.set(WebSocketConstant.ARGUMENTS_FIELD_NAME, argumentsNode);
|
||||
|
||||
// 创建模拟服务
|
||||
MockEntityService mockService = mock(MockEntityService.class);
|
||||
MockModel mockModel = new MockModel();
|
||||
mockModel.setId(1);
|
||||
mockModel.setName("test-name");
|
||||
|
||||
when(mockService.getById(1)).thenReturn(mockModel);
|
||||
doNothing().when(mockService).delete(mockModel);
|
||||
|
||||
// 模拟SpringApp.getBean
|
||||
try (MockedStatic<SpringApp> mockedStatic = mockStatic(SpringApp.class)) {
|
||||
mockedStatic.when(() -> SpringApp.getBean("mockService")).thenReturn(mockService);
|
||||
|
||||
// 使用反射调用私有方法
|
||||
Method method = WebSocketServerCallbackManager.class.getDeclaredMethod(
|
||||
"handleAsMessageCallback", SessionInfo.class, String.class, JsonNode.class);
|
||||
method.setAccessible(true);
|
||||
Object result = method.invoke(callbackManager, sessionInfo, "test-message-id", jsonNode);
|
||||
|
||||
// 验证结果
|
||||
assertNotNull(result);
|
||||
assertEquals(mockModel, result);
|
||||
|
||||
// 验证方法调用
|
||||
verify(mockService, times(1)).getById(1);
|
||||
verify(mockService, times(1)).delete(mockModel);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试send方法,验证消息发送功能
|
||||
*/
|
||||
@Test
|
||||
void testSend() throws Exception {
|
||||
// 准备测试数据
|
||||
MockModel mockModel = new MockModel();
|
||||
mockModel.setId(1);
|
||||
mockModel.setName("test-name");
|
||||
|
||||
// 使用反射调用私有方法
|
||||
Method method = WebSocketServerCallbackManager.class.getDeclaredMethod(
|
||||
"send", SessionInfo.class, String.class, Object.class);
|
||||
method.setAccessible(true);
|
||||
method.invoke(callbackManager, sessionInfo, "test-message-id", mockModel);
|
||||
|
||||
// 验证方法调用
|
||||
Map<String, Object> expectedMap = new HashMap<>();
|
||||
expectedMap.put("data", mockModel);
|
||||
expectedMap.put(WebSocketConstant.MESSAGE_ID_FIELD_NAME, "test-message-id");
|
||||
expectedMap.put(WebSocketConstant.SUCCESS_FIELD_NAME, true);
|
||||
|
||||
verify(sessionInfo, times(1)).send(expectedMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试send方法,验证Voable对象的处理
|
||||
*/
|
||||
@Test
|
||||
void testSend_Voable() throws Exception {
|
||||
// 准备测试数据
|
||||
MockModelWithVo mockModel = new MockModelWithVo();
|
||||
mockModel.setId(1);
|
||||
mockModel.setName("test-name");
|
||||
|
||||
// 使用反射调用私有方法
|
||||
Method method = WebSocketServerCallbackManager.class.getDeclaredMethod(
|
||||
"send", SessionInfo.class, String.class, Object.class);
|
||||
method.setAccessible(true);
|
||||
method.invoke(callbackManager, sessionInfo, "test-message-id", mockModel);
|
||||
|
||||
// 使用ArgumentCaptor捕获实际传入的参数
|
||||
ArgumentCaptor<Map<String, Object>> captor = ArgumentCaptor.forClass(Map.class);
|
||||
verify(sessionInfo, times(1)).send(captor.capture());
|
||||
|
||||
// 验证捕获的参数
|
||||
Map<String, Object> actualMap = captor.getValue();
|
||||
assertEquals("test-message-id", actualMap.get(WebSocketConstant.MESSAGE_ID_FIELD_NAME));
|
||||
assertEquals(true, actualMap.get(WebSocketConstant.SUCCESS_FIELD_NAME));
|
||||
|
||||
// 验证data字段是MockVo类型
|
||||
assertTrue(actualMap.get("data") instanceof MockVo);
|
||||
MockVo actualVo = (MockVo) actualMap.get("data");
|
||||
|
||||
// 验证MockVo的内容
|
||||
MockVo expectedVo = mockModel.toVo();
|
||||
assertEquals(expectedVo.getId(), actualVo.getId());
|
||||
assertEquals(expectedVo.getName(), actualVo.getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试createNewEntity方法,验证新实体创建功能
|
||||
*/
|
||||
@Test
|
||||
void testCreateNewEntity() throws Exception {
|
||||
// 创建模拟服务
|
||||
ConcreteMockEntityService mockService = new ConcreteMockEntityService();
|
||||
|
||||
// 使用反射获取createNewEntity方法
|
||||
Method method = WebSocketServerCallbackManager.class.getDeclaredMethod("createNewEntity", IEntityService.class);
|
||||
method.setAccessible(true);
|
||||
|
||||
// 执行方法
|
||||
Object result = method.invoke(callbackManager, mockService);
|
||||
|
||||
// 验证结果
|
||||
assertNotNull(result);
|
||||
assertTrue(result instanceof MockModel);
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试getTargetClass方法,验证获取原始类功能
|
||||
*/
|
||||
@Test
|
||||
void testGetTargetClass() throws Exception {
|
||||
// 创建模拟CGLIB代理类
|
||||
Class<?> cglibProxyClass = createMockCglibProxyClass();
|
||||
|
||||
// 使用反射获取getTargetClass方法
|
||||
Method method = WebSocketServerCallbackManager.class.getDeclaredMethod("getTargetClass", Class.class);
|
||||
method.setAccessible(true);
|
||||
|
||||
// 执行方法
|
||||
Class<?> result = (Class<?>) method.invoke(callbackManager, cglibProxyClass);
|
||||
|
||||
// 验证结果
|
||||
assertNotNull(result);
|
||||
assertEquals(MockModel.class, result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 模拟的QueryService实现
|
||||
*/
|
||||
private interface MockQueryService extends QueryService<MockVo> {
|
||||
@Override
|
||||
Page<MockVo> findAll(JsonNode paramsNode, Pageable pageable);
|
||||
|
||||
@Override
|
||||
MockVo findById(Integer id);
|
||||
}
|
||||
|
||||
/**
|
||||
* 模拟的IEntityService实现
|
||||
*/
|
||||
private interface MockEntityService extends IEntityService<MockModel>, VoableService<MockModel, MockVo> {
|
||||
@Override
|
||||
MockModel getById(Integer id);
|
||||
|
||||
@Override
|
||||
Page<MockModel> findAll(Specification<MockModel> spec, Pageable pageable);
|
||||
|
||||
@Override
|
||||
Specification<MockModel> getSpecification(String searchText);
|
||||
|
||||
@Override
|
||||
void delete(MockModel entity);
|
||||
|
||||
@Override
|
||||
MockModel save(MockModel entity);
|
||||
|
||||
@Override
|
||||
void updateByVo(MockModel model, MockVo vo);
|
||||
}
|
||||
|
||||
/**
|
||||
* 具体的MockEntityService实现,用于测试createNewEntity方法
|
||||
*/
|
||||
private static class ConcreteMockEntityService implements IEntityService<MockModel>, VoableService<MockModel, MockVo> {
|
||||
@Override
|
||||
public MockModel getById(Integer id) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<MockModel> findAll(Specification<MockModel> spec, Pageable pageable) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Specification<MockModel> getSpecification(String searchText) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(MockModel entity) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public MockModel save(MockModel entity) {
|
||||
return entity; // 返回传入的实体对象
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateByVo(MockModel model, MockVo vo) {
|
||||
if (model != null && vo != null) {
|
||||
model.setName(vo.getName());
|
||||
if (vo.getId() != null) {
|
||||
model.setId(vo.getId());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 模拟的模型类
|
||||
*/
|
||||
public static class MockModel {
|
||||
private Integer id;
|
||||
private String name;
|
||||
|
||||
public Integer getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Integer id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 模拟的VO类
|
||||
*/
|
||||
private static class MockVo {
|
||||
private Integer id;
|
||||
private String name;
|
||||
|
||||
public Integer getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Integer id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 实现了Voable接口的模拟模型类
|
||||
*/
|
||||
private static class MockModelWithVo extends MockModel implements Voable<MockVo> {
|
||||
@Override
|
||||
public MockVo toVo() {
|
||||
MockVo vo = new MockVo();
|
||||
vo.setId(getId());
|
||||
vo.setName(getName());
|
||||
return vo;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建模拟的CGLIB代理类
|
||||
*/
|
||||
private Class<?> createMockCglibProxyClass() {
|
||||
// 这里创建一个简单的模拟CGLIB代理类,实际项目中可能需要更复杂的实现
|
||||
return MockModel$$SpringCGLIB$$0.class;
|
||||
}
|
||||
|
||||
/**
|
||||
* 模拟的CGLIB代理类
|
||||
*/
|
||||
private static class MockModel$$SpringCGLIB$$0 extends MockModel {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,256 @@
|
||||
package com.ecep.contract.util;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.data.redis.core.ScanOptions;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.Mockito.*;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
/**
|
||||
* RedisCacheCleaner的测试类,用于验证缓存清理功能
|
||||
*/
|
||||
class RedisCacheCleanerTest {
|
||||
|
||||
@Mock
|
||||
private RedisTemplate<String, Object> redisTemplate;
|
||||
|
||||
@InjectMocks
|
||||
private RedisCacheCleaner cacheCleaner;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
MockitoAnnotations.openMocks(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试cleanCacheByPattern方法,验证按模式清理缓存功能
|
||||
*/
|
||||
@Test
|
||||
void testCleanCacheByPattern() {
|
||||
// 准备测试数据
|
||||
String pattern = "contract:*";
|
||||
Set<String> keys = new HashSet<>();
|
||||
keys.add("contract:1");
|
||||
keys.add("contract:2");
|
||||
keys.add("contract:3");
|
||||
|
||||
// 模拟RedisTemplate的scan和delete方法
|
||||
when(redisTemplate.keys(anyString())).thenReturn(keys);
|
||||
when(redisTemplate.delete(anyCollection())).thenReturn(1L);
|
||||
|
||||
// 执行方法
|
||||
int result = cacheCleaner.cleanCacheByPattern(pattern);
|
||||
|
||||
// 验证结果
|
||||
assertEquals(3, result);
|
||||
|
||||
// 验证方法调用
|
||||
verify(redisTemplate, times(1)).keys(pattern);
|
||||
verify(redisTemplate, times(1)).delete(keys);
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试cleanCacheByPattern方法,验证没有匹配键的情况
|
||||
*/
|
||||
@Test
|
||||
void testCleanCacheByPattern_NoKeysFound() {
|
||||
// 准备测试数据
|
||||
String pattern = "non_existent:*";
|
||||
|
||||
// 模拟RedisTemplate的keys方法返回空集合
|
||||
when(redisTemplate.keys(anyString())).thenReturn(new HashSet<>());
|
||||
|
||||
// 执行方法
|
||||
int result = cacheCleaner.cleanCacheByPattern(pattern);
|
||||
|
||||
// 验证结果
|
||||
assertEquals(0, result);
|
||||
|
||||
// 验证方法调用
|
||||
verify(redisTemplate, times(1)).keys(pattern);
|
||||
verify(redisTemplate, times(0)).delete(anyCollection());
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试cleanCacheByPattern方法,验证RedisTemplate返回null的情况
|
||||
*/
|
||||
@Test
|
||||
void testCleanCacheByPattern_NullKeys() {
|
||||
// 准备测试数据
|
||||
String pattern = "test:*";
|
||||
|
||||
// 模拟RedisTemplate的keys方法返回null
|
||||
when(redisTemplate.keys(anyString())).thenReturn(null);
|
||||
|
||||
// 执行方法
|
||||
int result = cacheCleaner.cleanCacheByPattern(pattern);
|
||||
|
||||
// 验证结果
|
||||
assertEquals(0, result);
|
||||
|
||||
// 验证方法调用
|
||||
verify(redisTemplate, times(1)).keys(pattern);
|
||||
verify(redisTemplate, times(0)).delete(anyCollection());
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试cleanAllCache方法,验证清理所有缓存功能
|
||||
*/
|
||||
@Test
|
||||
void testCleanAllCache() {
|
||||
// 准备测试数据
|
||||
Set<String> keys = new HashSet<>();
|
||||
keys.add("contract:1");
|
||||
keys.add("project:1");
|
||||
keys.add("user:1");
|
||||
|
||||
// 模拟RedisTemplate的scan和delete方法
|
||||
when(redisTemplate.keys("*"))
|
||||
.thenReturn(keys)
|
||||
.thenReturn(new HashSet<>()); // 第二次调用返回空,结束循环
|
||||
|
||||
when(redisTemplate.delete(anyCollection())).thenReturn(1L);
|
||||
|
||||
// 执行方法
|
||||
int result = cacheCleaner.cleanAllCache();
|
||||
|
||||
// 验证结果
|
||||
assertEquals(3, result);
|
||||
|
||||
// 验证方法调用
|
||||
verify(redisTemplate, atLeastOnce()).keys("*");
|
||||
verify(redisTemplate, times(1)).delete(keys);
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试cleanCache方法,验证清理指定键的缓存功能
|
||||
*/
|
||||
@Test
|
||||
void testCleanCache() {
|
||||
// 准备测试数据
|
||||
String key = "contract:1";
|
||||
|
||||
// 模拟RedisTemplate的delete方法
|
||||
when(redisTemplate.delete(anyString())).thenReturn(true);
|
||||
|
||||
// 执行方法
|
||||
boolean result = cacheCleaner.cleanCache(key);
|
||||
|
||||
// 验证结果
|
||||
assertTrue(result);
|
||||
|
||||
// 验证方法调用
|
||||
verify(redisTemplate, times(1)).delete(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试cleanCache方法,验证清理不存在键的缓存功能
|
||||
*/
|
||||
@Test
|
||||
void testCleanCache_KeyNotFound() {
|
||||
// 准备测试数据
|
||||
String key = "non_existent:1";
|
||||
|
||||
// 模拟RedisTemplate的delete方法返回false
|
||||
when(redisTemplate.delete(anyString())).thenReturn(false);
|
||||
|
||||
// 执行方法
|
||||
boolean result = cacheCleaner.cleanCache(key);
|
||||
|
||||
// 验证结果
|
||||
assertFalse(result);
|
||||
|
||||
// 验证方法调用
|
||||
verify(redisTemplate, times(1)).delete(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试cleanMultipleKeys方法,验证清理多个键的缓存功能
|
||||
*/
|
||||
@Test
|
||||
void testCleanMultipleKeys() {
|
||||
// 准备测试数据
|
||||
String[] keys = {"contract:1", "contract:2", "contract:3"};
|
||||
|
||||
// 模拟RedisTemplate的delete方法
|
||||
when(redisTemplate.delete(anyCollection())).thenReturn(3L);
|
||||
|
||||
// 执行方法
|
||||
long result = cacheCleaner.cleanMultipleKeys(keys);
|
||||
|
||||
// 验证结果
|
||||
assertEquals(3, result);
|
||||
|
||||
// 验证方法调用
|
||||
verify(redisTemplate, times(1)).delete(anyCollection());
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试cleanServiceCache方法,验证清理特定服务的缓存功能
|
||||
*/
|
||||
@Test
|
||||
void testCleanServiceCache() {
|
||||
// 准备测试数据
|
||||
String serviceName = "ContractService";
|
||||
Set<String> keys = new HashSet<>();
|
||||
keys.add("contract:1");
|
||||
keys.add("contract:2");
|
||||
|
||||
// 模拟RedisTemplate的keys和delete方法
|
||||
when(redisTemplate.keys("contract:*")).thenReturn(keys);
|
||||
when(redisTemplate.delete(anyCollection())).thenReturn(1L);
|
||||
|
||||
// 执行方法
|
||||
int result = cacheCleaner.cleanServiceCache(serviceName);
|
||||
|
||||
// 验证结果
|
||||
assertEquals(2, result);
|
||||
|
||||
// 验证方法调用
|
||||
verify(redisTemplate, times(1)).keys("contract:*");
|
||||
verify(redisTemplate, times(1)).delete(keys);
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试cleanAllServiceCache方法,验证清理所有服务相关缓存功能
|
||||
*/
|
||||
@Test
|
||||
void testCleanAllServiceCache() {
|
||||
// 准备测试数据
|
||||
String[] serviceNames = {"ContractService", "ProjectService", "UserService"};
|
||||
Set<String> contractKeys = new HashSet<>();
|
||||
contractKeys.add("contract:1");
|
||||
Set<String> projectKeys = new HashSet<>();
|
||||
projectKeys.add("project:1");
|
||||
Set<String> userKeys = new HashSet<>();
|
||||
userKeys.add("user:1");
|
||||
|
||||
// 模拟RedisTemplate的keys和delete方法
|
||||
when(redisTemplate.keys("contract:*")).thenReturn(contractKeys);
|
||||
when(redisTemplate.keys("project:*")).thenReturn(projectKeys);
|
||||
when(redisTemplate.keys("user:*")).thenReturn(userKeys);
|
||||
when(redisTemplate.delete(anyCollection())).thenReturn(1L);
|
||||
|
||||
// 执行方法
|
||||
int result = cacheCleaner.cleanAllServiceCache(serviceNames);
|
||||
|
||||
// 验证结果
|
||||
assertEquals(3, result);
|
||||
|
||||
// 验证方法调用
|
||||
verify(redisTemplate, times(1)).keys("contract:*");
|
||||
verify(redisTemplate, times(1)).keys("project:*");
|
||||
verify(redisTemplate, times(1)).keys("user:*");
|
||||
verify(redisTemplate, times(3)).delete(anyCollection());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user