# OpPlugin 操作插件测试模式

## 适用范围

`<模块组>-opplugin` 工程中继承 `AbstractOperationServicePlugIn` 的操作插件类。
操作插件在单据操作（保存/提交/审核等）的各生命周期阶段介入处理。

## 测试文件位置

```
<模块组>-opplugin/src/test/java/kd/<包路径>/opplugin/{module}/XxxOpTest.java
```

## 操作插件生命周期方法

| 方法 | 触发时机 | 测试重点 |
|------|---------|---------|
| `onPreparePropertys` | 准备加载字段 | 验证字段键列表 |
| `onAddValidators` | 添加校验器 | 验证注册的校验器 |
| `beforeExecuteOperationTransaction` | 事务前 | 验证前置逻辑 |
| `beginOperationTransaction` | 事务开始 | 验证核心业务逻辑 |
| `endOperationTransaction` | 事务结束 | 验证后置处理 |
| `afterExecuteOperationTransaction` | 事务后 | 验证收尾逻辑 |

## 基类选择

| 场景 | 基类 |
|------|------|
| 简单操作插件 | `AbstractJunitNoDependenciesResManagerTest` |
| 需要通用操作插件框架 | 继承 `OpBaseTest`（提供 registerOpPlugin 方法） |

## 模板结构（直接方式）

```java
package kd.<包路径>.opplugin.modulename;

import kd.bos.dataentity.OperateOption;
import kd.bos.dataentity.entity.DynamicObject;
import kd.bos.entity.operate.result.OperationResult;
import kd.bos.entity.plugin.args.BeginOperationTransactionArgs;
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 XxxOpTest extends AbstractJunitNoDependenciesResManagerTest {

    MockedStatic<SomeHelper> someHelper;
    private BeginOperationTransactionArgs beginArgs;

    @Before
    public void before() {
        someHelper = mockStatic(SomeHelper.class);

        // 构建测试单据数据
        DynamicObject[] bills = getBills();

        // Mock BeginOperationTransactionArgs
        beginArgs = mock(BeginOperationTransactionArgs.class);
        when(beginArgs.getDataEntities()).thenReturn(bills);
        when(beginArgs.getOperationKey()).thenReturn("save");
    }

    @After
    public void after() {
        someHelper.close();
    }

    public void reset() {
        someHelper.reset();
    }

    private DynamicObject[] getBills() {
        DynamicObject bill = new DynamicObjectMocker()
            .add("id", 1L)
            .add("billno", "TEST-001")
            .add("billstatus", "A")
            .getObject();
        return new DynamicObject[]{bill};
    }

    @UnittestCaseInfo(
        author = "<name> <<name>@kingdee.com>",
        title = "Test begin operation transaction success",
        targetClass = "kd.<包路径>.opplugin.modulename.XxxOp",
        targetMethod = "beginoperationtransaction",
        lastUpdateTime = "yyyy-MM-dd HH:mm:ss",
        lastUpdateAuthor = "<name> <<name>@kingdee.com>",
        methodSignature = "public void beginOperationTransaction(BeginOperationTransactionArgs e)",
        testPoints = {"Functionality"},
        description = "Test begin operation transaction success"
    )
    @Test
    @DisplayName("测试事务开始：成功场景")
    public void testBeginOperationTransaction_Success() {
        //step 创建操作插件实例
        OperationResult operation = mock(OperationResult.class);
        when(operation.isSuccess()).thenReturn(true);

        XxxOp op = new XxxOp() {
            @Override
            public OperationResult getOperationResult() {
                return operation;
            }
        };
        OperateOption option = OperateOption.create();
        op.setContext(null, null, option);

        //step 执行方法
        op.beginOperationTransaction(beginArgs);

        //assert 验证预期行为
        someHelper.verify(() -> SomeHelper.someMethod(any()), times(1));
        reset();
    }
}
```

## 使用 OpBaseTest 基类方式

```java
public class XxxOpTest extends OpBaseTest {

    MockedStatic<SomeHelper> someHelper;

    @Override
    @Before
    public void before() {
        super.before();  // 初始化 MainEntityType
        someHelper = mockStatic(SomeHelper.class);
    }

    @Override
    @After
    public void after() {
        super.after();
        someHelper.close();
    }

    @Override
    protected AbstractOperationServicePlugIn buildOpPlugin() {
        return new XxxOp();
    }

    @Override
    protected DynamicObject[] buildData() {
        return new DynamicObject[]{
            new DynamicObjectMocker()
                .add("id", 1L)
                .add("billno", "TEST-001")
                .getObject()
        };
    }

    @Test
    public void testPlugin() {
        //step 注册操作插件（自动调用 onPreparePropertys 和 onAddValidators）
        XxxOp op = registerOpPlugin();

        //step 测试事务方法
        BeginOperationTransactionArgs args = createBeginOperationTransactionArgs("save");
        op.beginOperationTransaction(args);

        //assert 验证结果
        ...
    }
}
```

## OpBaseTest 提供的工具方法

| 方法 | 返回值 | 说明 |
|------|--------|------|
| `registerOpPlugin()` | T (插件实例) | 注册并初始化操作插件 |
| `createBeforeOperationArgs(opKey)` | BeforeOperationArgs | 创建事务前参数 |
| `createBeginOperationTransactionArgs(opKey)` | BeginOperationTransactionArgs | 创建事务开始参数 |
| `createEndOperationTransactionArgs(opKey)` | EndOperationTransactionArgs | 创建事务结束参数 |
| `createAfterOperationArgs(opKey)` | AfterOperationArgs | 创建事务后参数 |
| `getValidators()` | List | 获取注册的校验器列表 |
| `getFieldKeys()` | List | 获取准备的字段键列表 |

## 测试重写 getOperationResult 的技巧

操作插件中经常需要根据 `getOperationResult()` 结果做不同处理，
用匿名子类方式注入 Mock 的 OperationResult：

```java
XxxOp op = new XxxOp() {
    @Override
    public OperationResult getOperationResult() {
        OperationResult result = mock(OperationResult.class);
        when(result.isSuccess()).thenReturn(true);
        return result;
    }
};
```

## try-with-resources 模式

对于只在单个测试方法内使用的 MockedStatic，推荐 try-with-resources：

```java
@Test
public void testMethod() {
    try (MockedStatic<OperationHelper> operationHelper = mockStatic(OperationHelper.class);
         MockedStatic<ProcessPlanHelper> processPlanHelper = mockStatic(ProcessPlanHelper.class)) {

        //step 配置 Mock
        operationHelper.when(() -> OperationHelper.validator(any())).thenReturn(resultMap);

        //step 执行
        op.beforeExecuteOperationTransaction(args);

        //assert 验证
        assertTrue(condition);
    }
    // MockedStatic 自动关闭
}
```
