# Business Helper 测试模式

## 适用范围

`<模块组>-business` 工程中的静态帮助类，如 `XxxHelper`、`XxxService` 等。
这些类通常包含大量 `public static` 方法，内部调用各种 ServiceHelper 静态方法。

## 测试文件位置

```
<模块组>-business/src/test/java/kd/<包路径>/business/XxxHelperTest.java
```

与被测类保持相同的包路径。

## 基类选择

| 场景 | 基类 |
|------|------|
| 不需要 ResManager | `AbstractJunitNoDependenciesTest` |
| 需要 ResManager（多语言） | `AbstractJunitNoDependenciesResManagerTest` |
| 需要通用 Mock 集合 | 继承项目内 `BaseTest`（已封装常用 Mock） |

## 模板结构

```java
package kd.<包路径>.business;

import kd.bos.dataentity.entity.DynamicObject;
import kd.bos.form.unittest.DisplayName;
import kd.bos.servicehelper.QueryServiceHelper;
import kd.bos.servicehelper.BusinessDataServiceHelper;
import kd.bos.test.ext.annotaions.UnittestCaseInfo;
import kd.bos.unittest.AbstractJunitNoDependenciesResManagerTest;
import kd.bos.unittest.mock.DynamicObjectMocker;
import kd.bos.unittest.mock.ReflectHelper;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.mockito.MockedStatic;
import org.mockito.Mockito;

import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.mockStatic;

/**
 * XxxHelper 单元测试
 * @author 作者
 * @version 1.0
 * @date YYYY/MM/DD
 */
public class XxxHelperTest extends AbstractJunitNoDependenciesResManagerTest {

    // 1. 声明需要 Mock 的静态类
    MockedStatic<QueryServiceHelper> queryServiceHelper;
    MockedStatic<BusinessDataServiceHelper> businessDataServiceHelper;
    // 根据被测方法实际依赖添加更多...

    @Before
    public void before() {
        // 2. 初始化 MockedStatic
        queryServiceHelper = mockStatic(QueryServiceHelper.class);
        businessDataServiceHelper = mockStatic(BusinessDataServiceHelper.class);
    }

    @After
    public void after() {
        // 3. 关闭所有 MockedStatic（建议，防止状态污染）
        queryServiceHelper.close();
        businessDataServiceHelper.close();
    }

    public void reset() {
        // 4. 可选：在每个测试方法末尾重置状态
        queryServiceHelper.reset();
        businessDataServiceHelper.reset();
    }

    // ========== 测试方法 ==========

    @UnittestCaseInfo(
        author = "<name> <<name>@kingdee.com>",
        title = "Test method description",
        targetClass = "kd.<包路径>.business.XxxHelper",
        targetMethod = "methodname",
        lastUpdateTime = "yyyy-MM-dd HH:mm:ss",
        lastUpdateAuthor = "<name> <<name>@kingdee.com>",
        methodSignature = "public static ReturnType methodName(ParamType param)",
        testPoints = {"Functionality"},
        description = "Test method description"
    )
    @Test
    @DisplayName("测试场景：正常路径")
    public void testMethodName_NormalCase() {
        //step 构建 Mock 返回数据
        queryServiceHelper.when(() -> QueryServiceHelper.query(anyString(), anyString(), any()))
            .thenReturn(new DynamicObjectMocker()
                .add("id", 1L)
                .add("billno", "TEST-001")
                .getCollection());

        //step 执行被测方法
        Object result = XxxHelper.targetMethod(param);

        //assert 断言结果
        assertNotNull(result);
        assertEquals(expectedValue, result);
        reset();
    }
}
```

## 关键技巧

### 1. 测试 protected 方法

```java
@Test
public void testProtectedMethod() {
    XxxHelper instance = new XxxHelper();
    Object result = ReflectHelper.invokeProtectedMethod(
        XxxHelper.class, instance, "protectedMethodName", param1, param2);
    assertEquals(expected, result);
}
```

### 2. 测试 private static 方法

```java
@Test
public void testPrivateStaticMethod() {
    ReflectHelper.invokeStaticMethod(
        XxxHelper.class, "privateStaticMethodName", param1, param2);
    // 通过 verify 或 side-effect 断言
}
```

### 3. 使用项目 BaseTest 基类

如果被测方法依赖多个常见服务（OrgUnitServiceHelper、BillTypeParamHelper 等），
直接继承 `BaseTest`（在 `<模块组>-business/src/test/java/` 下查找）可省去重复的 Mock 初始化代码。BaseTest 已预置：

- OrgUnitServiceHelper
- BillTypeParamHelper
- ResManager（thenAnswer 返回第一个参数）
- SystemParamServiceHelper
- QueryServiceHelper
- BusinessDataServiceHelper
- OperationServiceHelper
- BaseDataServiceHelper
- SaveServiceHelper

### 4. CommonMockObject 工具

> 此为通用工具，详见 SKILL.md 2.9 节。此处展示 business 工程的典型用法。

```java
// 获取 Mock 的 IDataModel 和 IFormView
IDataModel model = CommonMockObject.getTestModel();
IFormView view = CommonMockObject.getTestView();

// 从 JSON 字符串构造 DynamicObject
DynamicObject obj = CommonMockObject.getDynByString("{\"id\":1,\"name\":\"test\"}");

// 从 Map 构造 DynamicObjectMocker（支持嵌套）
DynamicObjectMocker mocker = CommonMockObject.getDynMockerByMap(map);
```

### 5. Mock 静态方法的 thenAnswer 模式

> 此为通用技巧，详见 SKILL.md 2.10 节。此处展示 business 工程的典型用法。

当需要根据入参动态返回时：

```java
queryServiceHelper.when(() -> QueryServiceHelper.query(anyString(), anyString(), any()))
    .thenAnswer(invocation -> {
        String entityName = invocation.getArgument(0);
        if ("mmc_processplan".equals(entityName)) {
            return mockProcessPlanData();
        }
        return new DynamicObjectCollection();
    });
```
