# ConvertPlugin 转换插件测试模式

## 适用范围

`<模块组>-opplugin` 工程中的 BOTP 转换插件（下推/反写插件），如：
- 继承 `AbstractConvertPlugIn` 的类
- 实现 `afterConvert`、`beforeBuildRowCondition`、`afterBuildDrawFilter` 等方法的类

## 测试文件位置

```
<模块组>-opplugin/src/test/java/kd/<包路径>/opplugin/convertplugin/XxxConvertPluginTest.java
```

或

```
<模块组>-opplugin/src/test/java/kd/<包路径>/opplugin/common/botp/XxxBotpConvertPluginTest.java
```

## 模板结构

```java
package kd.<包路径>.opplugin.convertplugin;

import kd.bos.dataentity.entity.DynamicObject;
import kd.bos.entity.BillEntityType;
import kd.bos.entity.ExtendedDataEntity;
import kd.bos.entity.botp.runtime.ConvertOperationResult;
import kd.bos.form.unittest.DisplayName;
import kd.bos.test.ext.annotaions.UnittestCaseInfo;
import kd.bos.unittest.AbstractJunitNoDependenciesResManagerTest;
import kd.bos.unittest.mock.DynamicObjectMocker;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.mockito.MockedStatic;

import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.*;

public class XxxConvertPluginTest extends AbstractJunitNoDependenciesResManagerTest {

    MockedStatic<SomeHelper> someHelper;

    @Before
    public void before() {
        someHelper = mockStatic(SomeHelper.class);
    }

    @After
    public void after() {
        someHelper.close();
    }

    // 构建源单和目标单数据
    private ExtendedDataEntity[] buildSrcData() {
        DynamicObject srcBill = new DynamicObjectMocker()
            .add("id", 1L)
            .add("billno", "SRC-001")
            .add("entryentity", new DynamicObjectMocker()
                .add("id", 10L)
                .add("qty", BigDecimal.TEN)
                .getCollection())
            .getObject();
        return new ExtendedDataEntity[]{new ExtendedDataEntity(srcBill, 0, 0)};
    }

    private ExtendedDataEntity[] buildDestData() {
        DynamicObject destBill = new DynamicObjectMocker()
            .add("id", 2L)
            .add("entryentity", new DynamicObjectMocker()
                .add("id", 20L)
                .add("srcqty", BigDecimal.TEN)
                .getCollection())
            .getObject();
        return new ExtendedDataEntity[]{new ExtendedDataEntity(destBill, 0, 0)};
    }

    @UnittestCaseInfo(
        author = "<name> <<name>@kingdee.com>",
        title = "Test after convert normal",
        targetClass = "kd.<包路径>.opplugin.convertplugin.XxxConvertPlugin",
        targetMethod = "afterconvert",
        lastUpdateTime = "yyyy-MM-dd HH:mm:ss",
        lastUpdateAuthor = "<name> <<name>@kingdee.com>",
        methodSignature = "public void afterConvert(AfterConvertEventArgs e)",
        testPoints = {"Functionality"},
        description = "Test after convert normal"
    )
    @Test
    @DisplayName("转换后处理：正常场景")
    public void testAfterConvert_Normal() {
        //step 构建插件实例
        XxxConvertPlugin plugin = new XxxConvertPlugin();

        //step 构建转换结果
        ConvertOperationResult convertResult = mock(ConvertOperationResult.class);
        // 配置源单和目标单数据...

        //step 执行转换后方法
        plugin.afterConvert(afterConvertEventArgs);

        //assert 验证目标单字段是否被正确赋值
        assertEquals(expectedValue, destBill.get("fieldKey"));
    }
}
```

## 关键要点

1. **转换插件测试重点**是验证源单字段到目标单字段的映射逻辑
2. 通常需要 Mock `BillEntityType`、`ExtendedDataEntitySet` 等转换框架对象
3. 源单和目标单都用 DynamicObjectMocker 构造
4. 断言重点：目标单的字段值是否被正确设置

## Mock 转换框架对象示例

```java
// Mock BillEntityType
BillEntityType srcEntityType = mock(BillEntityType.class);
when(srcEntityType.getName()).thenReturn("mmc_outsourcesend");

BillEntityType destEntityType = mock(BillEntityType.class);
when(destEntityType.getName()).thenReturn("mmc_processsettle");

// Mock ExtendedDataEntitySet
ExtendedDataEntitySet srcDataEntitySet = mock(ExtendedDataEntitySet.class);
when(srcDataEntitySet.FindByPK(anyLong())).thenReturn(srcExtendedDataEntity);
```
