产品管理单元测试完善

This commit is contained in:
gaoqr
2025-10-14 13:34:39 +08:00
parent d0d626753f
commit dadaf09e4a
4 changed files with 468 additions and 5 deletions
@@ -11,7 +11,6 @@ import com.cf.imes.module.system.controller.admin.funds.products.vo.product.Prod
import com.cf.imes.module.system.dal.dataobject.funds.products.ProductsDO;
import com.cf.imes.module.system.dal.mysql.funds.products.ProductsMapper;
import com.cf.imes.module.system.enums.products.ProductStatusEnum;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
@@ -60,9 +59,7 @@ public class ProductsServiceImpl implements ProductsService {
// 校验产品是否存在
Integer status = validateProductPlateExists(updateReqVOId).getStatus();
// 校验产品名称唯一
if (StringUtils.isNotEmpty(productName)) {
validateProductNameUnique(updateReqVOId, productName);
}
validateProductNameUnique(updateReqVOId, productName);
// 当前状态是上架状态 && 目标状态不是下架 的操作提示已上架
if (ProductStatusEnum.PRODUCT_UPDATES.getStatus().equals(status) && !ProductStatusEnum.isProductDelis(updateReqVOStatus)) {
@@ -0,0 +1,216 @@
package com.cf.imes.module.system.controller.admin.funds.products;
import com.cf.imes.framework.common.pojo.PageParam;
import com.cf.imes.framework.common.pojo.PageResult;
import com.cf.imes.framework.security.core.util.SecurityFrameworkUtils;
import com.cf.imes.module.system.controller.admin.funds.products.vo.product.ProductPageReqVO;
import com.cf.imes.module.system.controller.admin.funds.products.vo.product.ProductSaveReqVO;
import com.cf.imes.module.system.dal.dataobject.funds.products.ProductsDO;
import com.cf.imes.module.system.enums.products.ProductStatusEnum;
import com.cf.imes.module.system.service.funds.products.ProductsService;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.http.MediaType;
import org.springframework.test.context.bean.override.mockito.MockitoBean;
import org.springframework.test.web.servlet.MockMvc;
import java.math.BigDecimal;
import java.util.List;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.mockStatic;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
/**
* @author Gqr
* @since 2025/10/14 11:16
*/
@WebMvcTest(controllers = ProductsController.class, excludeAutoConfiguration = SecurityAutoConfiguration.class)
public class ProductsControllerTest {
@Autowired
private MockMvc mockMvc;
@MockitoBean
private ProductsService productsService;
@Autowired
private ObjectMapper objectMapper;
@Test
void testCreateProducts() throws Exception {
ProductSaveReqVO vo = new ProductSaveReqVO();
vo.setProductName("新建产品");
vo.setPrice(new BigDecimal("199"));
vo.setStatus(ProductStatusEnum.PRODUCT_DELIS.getStatus());
// mock Service 返回
when(productsService.create(any())).thenReturn(100L);
mockMvc.perform(put("/system/products")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(vo)))
.andExpect(status().isOk())
.andExpect(jsonPath("$.data").value(100));
// 验证 Service 调用
verify(productsService, times(1)).create(any());
}
@Test
void testUpdateProducts() throws Exception {
ProductSaveReqVO vo = new ProductSaveReqVO();
vo.setId(1L);
vo.setPrice(new BigDecimal("199"));
vo.setProductName("更新名称");
// mock Service 方法 void 不做实际处理
doNothing().when(productsService).update(any());
mockMvc.perform(post("/system/products")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(vo)))
.andExpect(status().isOk())
.andExpect(jsonPath("$.data").value(true));
verify(productsService, times(1)).update(any());
}
@Test
void testFreezeProducts() throws Exception {
Long id = 1L;
doNothing().when(productsService).update(any());
mockMvc.perform(post("/system/products/freeze")
.param("id", id.toString()))
.andExpect(status().isOk())
.andExpect(jsonPath("$.data").value(true));
verify(productsService, times(1)).update(argThat(vo -> vo.getId().equals(id)
&& vo.getStatus().equals(ProductStatusEnum.PRODUCT_DELIS.getStatus())));
}
@Test
void testMeltProducts() throws Exception {
Long id = 2L;
doNothing().when(productsService).update(any());
mockMvc.perform(post("/system/products/melt")
.param("id", id.toString()))
.andExpect(status().isOk())
.andExpect(jsonPath("$.data").value(true));
verify(productsService, times(1)).update(argThat(vo -> vo.getId().equals(id)
&& vo.getStatus().equals(ProductStatusEnum.PRODUCT_UPDATES.getStatus())));
}
@Test
void testDeleteProducts() throws Exception {
Long id = 3L;
doNothing().when(productsService).delete(anyLong());
mockMvc.perform(delete("/system/products")
.param("id", id.toString()))
.andExpect(status().isOk())
.andExpect(jsonPath("$.data").value(true));
verify(productsService, times(1)).delete(id);
}
@Test
void testGetProducts() throws Exception {
Long id = 4L;
ProductsDO mockProduct = new ProductsDO()
.setId(id)
.setProductName("测试产品");
when(productsService.get(id)).thenReturn(mockProduct);
mockMvc.perform(get("/system/products/{id}", id))
.andExpect(status().isOk())
.andExpect(jsonPath("$.data.productName").value("测试产品"));
verify(productsService, times(1)).get(id);
}
@Test
void testGetProductsPage() throws Exception {
ProductPageReqVO reqVO = new ProductPageReqVO();
PageResult<ProductsDO> mockPage = new PageResult<>();
mockPage.setTotal(1L);
mockPage.setList(List.of(new ProductsDO().setId(1L).setProductName("分页产品")));
when(productsService.getPage(any())).thenReturn(mockPage);
mockMvc.perform(get("/system/products/page")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(reqVO)))
.andExpect(status().isOk())
.andExpect(jsonPath("$.data.list[0].productName").value("分页产品"));
verify(productsService, times(1)).getPage(any());
}
@Test
void testGetAvailableProductsPage() throws Exception {
ProductPageReqVO reqVO = new ProductPageReqVO();
PageResult<ProductsDO> mockPage = new PageResult<>();
mockPage.setTotal(1L);
mockPage.setList(List.of(new ProductsDO().setId(1L).setProductName("已上架产品")));
when(productsService.getPage(any())).thenReturn(mockPage);
// mock 管理端标识
try (var ignored = mockStatic(SecurityFrameworkUtils.class)) {
when(SecurityFrameworkUtils.isManageEndPoint()).thenReturn(true);
mockMvc.perform(get("/system/products/available")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(reqVO)))
.andExpect(status().isOk())
.andExpect(jsonPath("$.data.list[0].productName").value("已上架产品"));
verify(productsService, times(1)).getPage(any());
}
}
@Test
void testGetAvailableProductsPage_nonManager() throws Exception {
ProductPageReqVO reqVO = new ProductPageReqVO();
// 准备 mock 分页数据
PageResult<ProductsDO> mockPage = new PageResult<>();
mockPage.setTotal(1L);
mockPage.setList(List.of(new ProductsDO().setId(123L).setProductName("非管理端产品")));
when(productsService.getPage(any())).thenReturn(mockPage);
// mock SecurityFrameworkUtils 的静态方法
try (var securityMock = mockStatic(SecurityFrameworkUtils.class)) {
// 返回非管理端
securityMock.when(SecurityFrameworkUtils::isManageEndPoint).thenReturn(false);
// 返回登录的产品 ID
securityMock.when(SecurityFrameworkUtils::getProductId).thenReturn(123L);
mockMvc.perform(get("/system/products/available")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(reqVO)))
.andExpect(status().isOk())
.andExpect(jsonPath("$.data.list[0].productName").value("非管理端产品"));
// 验证 Service 调用时 pageReqVO 已经被设置 ID
verify(productsService, times(1)).getPage(argThat(vo -> vo.getId().equals(123L)
&& vo.getStatus().equals(ProductStatusEnum.PRODUCT_UPDATES.getStatus())
&& vo.getPageSize().equals(PageParam.PAGE_SIZE_NONE)));
}
}
}
@@ -0,0 +1,235 @@
package com.cf.imes.module.system.service.funds.products;
import com.cf.imes.framework.common.pojo.PageResult;
import com.cf.imes.framework.test.core.ut.BaseDbAndRedisUnitTest;
import com.cf.imes.module.system.controller.admin.funds.products.vo.product.ProductPageReqVO;
import com.cf.imes.module.system.controller.admin.funds.products.vo.product.ProductSaveReqVO;
import com.cf.imes.module.system.dal.dataobject.funds.products.ProductsDO;
import com.cf.imes.module.system.dal.mysql.funds.products.ProductsMapper;
import com.cf.imes.module.system.enums.products.ProductStatusEnum;
import jakarta.annotation.Resource;
import org.junit.jupiter.api.Test;
import org.springframework.context.annotation.Import;
import java.math.BigDecimal;
import static com.cf.imes.framework.common.util.object.ObjectUtils.cloneIgnoreId;
import static com.cf.imes.framework.test.core.util.AssertUtils.assertPojoEquals;
import static com.cf.imes.framework.test.core.util.AssertUtils.assertServiceException;
import static com.cf.imes.framework.test.core.util.RandomUtils.randomLongId;
import static com.cf.imes.framework.test.core.util.RandomUtils.randomPojo;
import static com.cf.imes.framework.test.core.util.RandomUtils.randomString;
import static com.cf.imes.module.system.enums.ErrorCodeConstants.PRODUCTS_IS_EXIST;
import static com.cf.imes.module.system.enums.ErrorCodeConstants.PRODUCTS_NO_DELETE;
import static com.cf.imes.module.system.enums.ErrorCodeConstants.PRODUCTS_NO_EXIST;
import static com.cf.imes.module.system.enums.ErrorCodeConstants.PRODUCTS_NO_UPDATE;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
/**
* @author Gqr
* @since 2025/10/14 9:46
*/
@Import(ProductsServiceImpl.class)
public class ProductsServiceImplTest extends BaseDbAndRedisUnitTest {
@Resource
private ProductsServiceImpl productsService;
@Resource
private ProductsMapper productsMapper;
@Test
void testCreateProductSuccess() {
// 构造请求对象
String productName = randomString();
ProductSaveReqVO req = new ProductSaveReqVO();
req.setProductName(productName);
req.setPrice(new BigDecimal("88.88"));
req.setStatus(ProductStatusEnum.PRODUCT_UPDATES.getStatus());
// 调用真实 Service 方法
Long id = productsService.create(req);
// 校验数据库插入结果
ProductsDO result = productsMapper.selectById(id);
assertNotNull(result);
assertEquals(productName, result.getProductName());
assertEquals(new BigDecimal("88.88"), result.getPrice());
assertEquals(ProductStatusEnum.PRODUCT_UPDATES.getStatus(), result.getStatus());
}
/**
* 下架更新为上架
*/
@Test
void testUpdate_takeoff_to_listed() {
ProductSaveReqVO createVO = new ProductSaveReqVO();
String productName = randomString();
createVO.setProductName(productName);
createVO.setPrice(new BigDecimal("88.88"));
createVO.setStatus(ProductStatusEnum.PRODUCT_DELIS.getStatus());
Long id = productsService.create(createVO);
ProductSaveReqVO updateVO = new ProductSaveReqVO();
updateVO.setId(id);
String newProductName = randomString();
updateVO.setProductName(newProductName);
updateVO.setStatus(ProductStatusEnum.PRODUCT_UPDATES.getStatus());
updateVO.setPrice(new BigDecimal("199.99"));
productsService.update(updateVO);
ProductsDO db = productsMapper.selectById(id);
assertThat(db.getProductName()).isEqualTo(newProductName);
assertThat(db.getPrice()).isEqualByComparingTo("199.99");
assertEquals(ProductStatusEnum.PRODUCT_UPDATES.getStatus(), db.getStatus());
}
/**
* 已上架更新
*/
@Test
void testUpdate_statusUpdatableThrowsException() {
ProductSaveReqVO dbProduct = new ProductSaveReqVO();
dbProduct.setProductName("产品");
dbProduct.setStatus(ProductStatusEnum.PRODUCT_UPDATES.getStatus());
Long id = productsService.create(dbProduct);
ProductSaveReqVO reqVO = new ProductSaveReqVO();
reqVO.setId(id);
reqVO.setStatus(ProductStatusEnum.PRODUCT_UPDATES.getStatus()); // 非下架
assertServiceException(() -> productsService.update(reqVO),
PRODUCTS_NO_UPDATE);
}
/**
* 已上架更新下架
*/
@Test
void testUpdate_listed_to_takeoff() {
ProductSaveReqVO createVO = new ProductSaveReqVO();
String productName = randomString();
createVO.setProductName(productName);
createVO.setPrice(new BigDecimal("88.88"));
createVO.setStatus(ProductStatusEnum.PRODUCT_UPDATES.getStatus());
Long id = productsService.create(createVO);
ProductSaveReqVO updateVO = new ProductSaveReqVO();
updateVO.setId(id);
String newProductName = randomString();
updateVO.setProductName(newProductName);
updateVO.setStatus(ProductStatusEnum.PRODUCT_DELIS.getStatus());
updateVO.setPrice(new BigDecimal("199.99"));
productsService.update(updateVO);
ProductsDO db = productsMapper.selectById(id);
assertThat(db.getProductName()).isEqualTo(newProductName);
assertThat(db.getPrice()).isEqualByComparingTo("199.99");
assertEquals(ProductStatusEnum.PRODUCT_DELIS.getStatus(), db.getStatus());
}
@Test
void testDelete() {
ProductSaveReqVO createVO = new ProductSaveReqVO();
createVO.setProductName(randomString());
createVO.setPrice(new BigDecimal("88.88"));
createVO.setStatus(ProductStatusEnum.PRODUCT_DELIS.getStatus());
Long id = productsService.create(createVO);
productsService.delete(id);
// 断言不存在了
assertNull(productsMapper.selectById(id));
}
@Test
void testDelete_listed() {
ProductSaveReqVO createVO = new ProductSaveReqVO();
createVO.setProductName(randomString());
createVO.setPrice(new BigDecimal("88.88"));
createVO.setStatus(ProductStatusEnum.PRODUCT_UPDATES.getStatus());
Long id = productsService.create(createVO);
assertServiceException(() -> productsService.delete(id),
PRODUCTS_NO_DELETE);
}
@Test
void testGet() {
ProductSaveReqVO createVO = new ProductSaveReqVO();
String productName = randomString();
createVO.setProductName(productName);
createVO.setPrice(new BigDecimal("88.88"));
createVO.setStatus(1);
Long id = productsService.create(createVO);
ProductsDO db = productsService.get(id);
assertThat(db).isNotNull();
assertThat(db.getProductName()).isEqualTo(productName);
}
@Test
void testGetProductPage() {
// ============ mock 数据 ============
ProductsDO dbProduct = randomPojo(ProductsDO.class, o -> { // 这个会被查到
o.setProductName("晨丰-软件");
o.setStatus(ProductStatusEnum.PRODUCT_UPDATES.getStatus()); // 假设0=上架
o.setDeleted(false);
o.setPrice(new BigDecimal("99.99"));
});
productsMapper.insert(dbProduct);
// 不匹配 productName
productsMapper.insert(cloneIgnoreId(dbProduct, o -> o.setProductName("测试产品")));
// 不匹配 status
productsMapper.insert(cloneIgnoreId(dbProduct, o -> o.setStatus(ProductStatusEnum.PRODUCT_DELIS.getStatus())));
// 被逻辑删除
productsMapper.insert(cloneIgnoreId(dbProduct, o -> o.setDeleted(true)));
// ============ 构造查询参数 ============
ProductPageReqVO reqVO = new ProductPageReqVO();
reqVO.setProductName("晨丰");
reqVO.setStatus(ProductStatusEnum.PRODUCT_UPDATES.getStatus());
reqVO.setPageNo(1);
reqVO.setPageSize(10);
// ============ 调用方法 ============
PageResult<ProductsDO> pageResult = productsService.getPage(reqVO);
// ============ 断言结果 ============
assertEquals(1, pageResult.getTotal(), "分页结果数量应为1");
assertEquals(1, pageResult.getList().size(), "分页列表长度应为1");
assertPojoEquals(dbProduct, pageResult.getList().get(0));
}
@Test
void testDuplicateNameThrowsException() {
ProductSaveReqVO createVO = new ProductSaveReqVO();
String productName = randomString();
createVO.setProductName(productName);
createVO.setPrice(new BigDecimal("88.88"));
createVO.setStatus(1);
productsService.create(createVO);
ProductSaveReqVO dup = new ProductSaveReqVO();
dup.setProductName(productName);
dup.setStatus(1);
dup.setPrice(new BigDecimal("50.00"));
assertServiceException(() -> productsService.create(dup),
PRODUCTS_IS_EXIST, productName);
}
@Test
void testGetNotExist() {
assertServiceException(() -> productsService.get(randomLongId()),
PRODUCTS_NO_EXIST);
}
}
@@ -683,4 +683,19 @@ CREATE TABLE IF NOT EXISTS "process_group" (
"update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
"deleted" bit NOT NULL DEFAULT FALSE,
PRIMARY KEY ("id")
) COMMENT '工序组表 process_group';
) COMMENT '工序组表 process_group';
CREATE TABLE IF NOT EXISTS "products" (
"id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
"product_name" varchar(64) NOT NULL,
"status" tinyint DEFAULT 1 NOT NULL,
"software_id" bigint NOT NULL,
"price" decimal(8,2),
"description" varchar,
"creator" varchar DEFAULT '',
"create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updater" varchar DEFAULT '',
"update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
"deleted" bit NOT NULL DEFAULT FALSE,
PRIMARY KEY ("id")
) COMMENT '产品表';