# Common Module 测试完整示例

> 展示 mmc-sfc-common 工程中三种测试类型的完整示例。

## 1. POJO 测试示例（InvokeResultTest）

```java
package kd.mmc.sfc.common.pojo;

import kd.bos.test.ext.annotaions.UnittestCaseInfo;
import org.junit.Test;

import static org.junit.Assert.*;

/**
 * InvokeResult 单元测试
 * @author zhang_san
 * @date 2026/02/05
 */
public class InvokeResultTest {

    @UnittestCaseInfo(
        author = "zhang_san <zhang_san@kingdee.com>",
        title = "Test success with data",
        targetClass = "kd.mmc.sfc.common.pojo.InvokeResult",
        targetMethod = "successwithdata",
        lastUpdateTime = "2026-02-05 15:58:24",
        lastUpdateAuthor = "zhang_san <zhang_san@kingdee.com>",
        methodSignature = "null",
        testPoints = {"Functionality"},
        description = "Test success with data"
    )
    @Test
    public void testSuccessWithData() {
        // step 准备测试数据
        String testData = "test data";

        // 执行被测方法
        InvokeResult result = InvokeResult.success(testData);

        // assert 验证结果
        assertTrue(result.isSuccess());
        assertEquals(testData, result.getData());
        assertNull(result.getMessage());
    }

    @Test
    public void testSuccessWithoutParams() {
        //step 执行被测方法
        InvokeResult result = InvokeResult.success();

        //assert 验证结果
        assertTrue(result.isSuccess());
        assertNull(result.getData());
    }

    @Test
    public void testFailureWithMessage() {
        // step
        InvokeResult result = InvokeResult.failure("error message");

        // assert
        assertFalse(result.isSuccess());
        assertEquals("error message", result.getMessage());
    }

    @Test
    public void testGetSetMessage() {
        // step
        InvokeResult result = new InvokeResult();
        result.setMessage("test message");

        // assert
        assertEquals("test message", result.getMessage());
    }

    @Test
    public void testToString() {
        // step
        InvokeResult result = new InvokeResult();
        result.setSuccess(true);
        result.setMessage("test message");
        result.setData("test data");

        String str = result.toString();

        // assert
        assertTrue(str.contains("success=true"));
        assertTrue(str.contains("message=test message"));
        assertTrue(str.contains("data=test data"));
    }
}
```

## 2. 枚举测试示例（PushOperationCodeEnumTest）

```java
package kd.mmc.sfc.common.enums;

import kd.bos.test.ext.annotaions.UnittestCaseInfo;
import kd.bos.unittest.AbstractJunitNoDependenciesTest;
import kd.mmc.sfc.common.processmaterial.ProcessMaterialConsts.OPERATION_CONST;
import org.junit.Test;

/**
 * PushOperationCodeEnum 单元测试
 * @author zhang_san
 * @date 2025/6/24
 */
public class PushOperationCodeEnumTest extends AbstractJunitNoDependenciesTest {

    @UnittestCaseInfo(
        author = "zhang_san <zhang_san@kingdee.com>",
        title = "Match push target bill empty operation key returns empty string",
        targetClass = "kd.mmc.sfc.common.enums.PushOperationCodeEnum",
        targetMethod = "matchpushtargetbill",
        lastUpdateTime = "2025-06-24 10:55:45",
        lastUpdateAuthor = "zhang_san <zhang_san@kingdee.com>",
        methodSignature = "public static String matchPushTargetBill(String operationKey)",
        testPoints = {"Functionality"},
        description = "Match push target bill empty operation key returns empty string"
    )
    @Test
    public void matchPushTargetBill_EmptyOperationKey_ReturnsEmptyString() {
        // step 空操作标识
        String result = PushOperationCodeEnum.matchPushTargetBill("");

        // assert
        assertEquals("", result);
    }

    @Test
    public void matchPushTargetBill_ValidOperationKey_ReturnsTargetBill() {
        // step 有效操作标识
        String result = PushOperationCodeEnum.matchPushTargetBill(
            OPERATION_CONST.RETURNAPPLYORDER);

        // assert 返回目标单据标识
        assertEquals("qcpp_manuinspecapply", result);
    }
}
```

## 3. 工具类测试示例（BillPushUtilTest）

```java
package kd.mmc.sfc.common.utils;

import kd.bos.dataentity.OperateOption;
import kd.bos.entity.botp.runtime.ConvertOperationResult;
import kd.bos.entity.botp.runtime.PushArgs;
import kd.bos.entity.botp.runtime.SourceBillReport;
import kd.bos.entity.datamodel.ListSelectedRow;
import kd.bos.servicehelper.botp.ConvertServiceHelper;
import kd.bos.test.ext.annotaions.UnittestCaseInfo;
import kd.mmc.sfc.common.BaseTest;  // common 包自带的 BaseTest
import org.junit.Test;
import org.mockito.MockedConstruction;
import org.mockito.MockedStatic;
import org.mockito.Mockito;

import java.util.*;
import java.util.stream.*;

import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.*;

/**
 * BillPushUtil 单元测试
 * @author zhang_san
 * @date 2025/4/21
 */
public class BillPushUtilTest extends BaseTest {

    // ========== 继承 BaseTest，自动拥有常用 Mock ==========
    // 无需自行声明 queryServiceHelper、businessDataServiceHelper 等

    @UnittestCaseInfo(
        author = "zhang_san <zhang_san@kingdee.com>",
        title = "Test do push",
        targetClass = "kd.mmc.sfc.common.utils.BillPushUtil",
        targetMethod = "dopush",
        lastUpdateTime = "2025-04-21 15:58:57",
        lastUpdateAuthor = "zhang_san <zhang_san@kingdee.com>",
        methodSignature = "public static ConvertOperationResult doPush(...)",
        testPoints = {"Functionality"},
        description = "Test do push"
    )
    @Test
    public void testDoPush() {
        // 使用 try-with-resources 管理方法级 Mock
        try (MockedStatic<ConvertServiceHelper> convertServiceHelper =
                 mockStatic(ConvertServiceHelper.class);
             // MockedConstruction：Mock 构造函数
             MockedConstruction<PushArgs> pushArgsMock =
                 Mockito.mockConstruction(PushArgs.class)) {

            //step 准备数据
            ListSelectedRow row = mock(ListSelectedRow.class);

            //step 执行
            BillPushUtil.doPush("sourcebill", "targetbill", "ruleid",
                new HashMap<>(), Stream.of(row).collect(Collectors.toList()));

            //assert 正常执行
            convertServiceHelper.when(() -> ConvertServiceHelper.push(any()))
                .thenReturn(null);

            //step 空列表返回 null
            ConvertOperationResult result = BillPushUtil.doPush(
                "sourcebill", "targetbill", "ruleid",
                new HashMap<>(), new ArrayList<>());

            //assert
            assertNull(result);
        }
    }

    @Test
    public void testGetErrorMessageFromConverResult() {
        //step 构建失败的转换结果
        ConvertOperationResult mockResult = mock(ConvertOperationResult.class);
        when(mockResult.isSuccess()).thenReturn(false);
        when(mockResult.getMessage()).thenReturn("Initial Error");

        SourceBillReport mockReport = mock(SourceBillReport.class);
        when(mockReport.isFullSuccess()).thenReturn(false);
        when(mockReport.getFailMessage()).thenReturn("Bill Error");
        when(mockResult.getBillReports()).thenReturn(
            new ArrayList<SourceBillReport>() {{ add(mockReport); }});

        //step 执行
        String errorMsg = BillPushUtil.getErrorMessageFromConverResult(mockResult);

        //assert 包含错误信息
        assertTrue(errorMsg.contains("Bill Error"));
    }
}
```

## 要点总结

### POJO 测试
- **零 Mock**，纯 Java 对象操作
- 覆盖 getter/setter、工厂方法、toString、Builder 模式
- 无需基类，测试最简洁

### 枚举测试
- 继承 `AbstractJunitNoDependenciesTest`
- 测试匹配方法的有效值 / 无效值 / 边界值
- 代码量最少的测试类型

### 工具类测试
- 继承 common 包 `BaseTest`（预置 10 个 MockedStatic）
- 额外 Mock 用 try-with-resources 管理
- `MockedConstruction` 可 Mock 类的构造函数（`new Xxx()` 内部创建的对象）
- 与 business 工程 BaseTest 的区别：common 的 BaseTest 多了 `EntityMetadataCache` Mock
