หลงจาก Blog ตอนที่แล้ว ที่ได้สร้าง Process ของการขอเบิกเงินไปแล้ว ตอนนี้เราต้องมา Proof ก่อนว่า Process ที่ทำนั้น มันใช้งานได้จริงๆ พร้อมที่นำไป Deploy ขึ้น Activiti Server ครับ โดยมีขั้นตอน ดังนี้
Table of Contents
Step 1 : กำหนดโครงสร้างไฟล์ที่เกี่ยวข้องกันก่อน
<Project Name>
|-src
|-main
| |-java [1]
| |-resources [2]
| |-diagrams
| |-Test.bpmn
|-test
|-java [3]
| |-ProcessTestApproveBugetProcess.java
|-resources [4]
|-Test.bpmn
|-activiti.cfg.xmlStep 2 : สร้างไฟล์ Config ที่เกี่ยวข้อง
- สร้างไฟล์แปลง Activiti Project ให้เป็น Maven Project ซึ่งมีไฟล์ pom.xml เพื่อเก็บ Dependency ต่างๆไว้ โดยสำหรับการ Test BPMN ต้องใช้ Dependency ที่จำเป็น ดังนี้
- activiti-engine : สำหรับ Execute BPMN
- JUnit : สำหรับทำ Unit Test
- MySQL Connector/J : ใช้เชื่อมต่อ Database ของ activiti-engine
- spring framework [Optional] : ในกรณีที่ต้องการใช้ Activiti กับ Spring
- และสุดท้าย หน้าตาของไฟล์ pom.xml มี Dependency ดังนี้ครับ
- สร้างไฟล์ activiti.cfg.xml โดยมีการกำหนดค่า ดังนี้
Step 3 : เอาไฟล์ Process มาใส่
- เอาไฟล์ Process ที่ทำไว้ จาก Blog ตอนที่แล้วจะเป็นไฟล์ Test.bpmn เอามาวางไว้ที่ Path .../src/test/resources/
- หรือ อาจจะอ้างไปที่ Path ของ project เลยก็ได้ครับ เช่น
private static String filename = "D:\\BFMN\\Workspace\\LearnBPMN\\src\\main\\resources\\diagrams\\Test.bpmn";
Step 4 : สร้าง Unit Test ขึ้นครับ
ตั้งโครงใหม่ ให้มันเข้ากับ Activiti 6.0 และ JUnit 5 กันก่อนครับ โดยใช้ Test แบ่งเป็นช่วงๆ ดังนี้
📌 ส่วน @BeforeAll - ทำงานตอน Execute Test ครั้งแรก โดยสร้าง ActivitiRule ขึ้นมา เพื่อเตรียม Activiti-Engine ให้พร้อม
public static ActivitiRule activitiRule;
public static ProcessInstance processInstance;
@BeforeAll
public static void init(){
System.out.println("Before All init() method called");
ProcessEngine processEngine = ProcessEngineConfiguration.createStandaloneInMemProcessEngineConfiguration()
.buildProcessEngine();
activitiRule = new ActivitiRule(processEngine);
}📌 ส่วน @BeforeEach และ @AfterEach เป็นส่วนที่ทำงานก่อน และหลัง Test Case แต่ละอัน
- @BeforeEach - ในที่นี้เป็นการ Deploy Process ที่เพิ่งสร้าง ให้ Activiti-Engine รู้จัก
@BeforeEach
public void DeployProcess(){
try {
System.out.println("=======================================");
System.out.println("Deploy Process");
RepositoryService repositoryService = activitiRule.getRepositoryService();
repositoryService.createDeployment().addInputStream("Test.bpmn", new FileInputStream(filename)).deploy();
RuntimeService runtimeService = activitiRule.getRuntimeService();
Map<String, Object> variableMap = new HashMap<String, Object>();
variableMap.put("budgetAmount", 100000.00);
variableMap.put("Approved", false);
processInstance = runtimeService.startProcessInstanceByKey("ApproveBudgetProcess", variableMap);
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}- @AfterEach - ในที่นี้เป็นการ ลบ หรือ Clear Process ที่เพิ่ง Deploy ออกจาก Activiti-Engine รู้จัก
@AfterEach
public void RemoveProcess(){
try {
RepositoryService repositoryService = activitiRule.getRepositoryService();
String deploymentID = repositoryService.createDeploymentQuery().singleResult().getId();
repositoryService.deleteDeployment(deploymentID, true);
Deployment deployment = repositoryService.createDeploymentQuery()
.singleResult();
assertNull(deployment);
ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
.singleResult();
assertNull(processDefinition);
RuntimeService runtimeService = activitiRule.getRuntimeService();
processInstance = runtimeService.createProcessInstanceQuery()
.singleResult();
assertNull(processInstance);
System.out.println("Remove Process");
System.out.println("=======================================");
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}📌 ส่วน @Test เป็น Test Case ที่เตรียมไว้ ซึ่งมี 5 Test Case ดังนี้
- TC1 test1ProcessIsStart - บอกว่า Process ที่ Deploy ไปพร้อมใช้งาน หรือไม่ ?
@Test
public void test1ProcessIsStart() throws Exception {
System.out.println("Execute - test1ProcessIsStart");
assertNotNull(processInstance.getId());
System.out.println("id " + processInstance.getId() + " " + processInstance.getProcessDefinitionId());
}- TC2 test2AssignValueInVariableBudgetAmount - ตอนที่สร้าง Process ขึ้นมา เราได้ยัดค่าตัวแปร BudgetAmount ลงไป โดยใน Test Case ดูจะดูว่า มันสามารถดึงค่าที่ได้ Pass เข้าไปได้ หรือไม่
@Test
public void test2AssignValueInVariableBudgetAmount() throws Exception {
System.out.println("Execute - test2AssignValueInVariableBudgetAmount");
String processInstanceID = processInstance.getId();
RuntimeService runtimeService = activitiRule.getRuntimeService();
Double budgetAmount = (Double) runtimeService.getVariable(processInstanceID, "budgetAmount");
assertNotNull(budgetAmount);
assertEquals(Double.valueOf(100000.00), budgetAmount);
System.out.println("Actual Value of budgetAmount: " + budgetAmount);
}- TC3 test3AssignValueInVariableApproved - ตอนที่สร้าง Process ขึ้นมา เราได้ยัดค่าตัวแปร Approved ลงไป โดยใน Test Case ดูจะดูว่า มันสามารถดึงค่าที่ได้ Pass เข้าไปได้ หรือไม่
@Test
public void test3AssignValueInVariableApproved() throws Exception{
System.out.println("Execute - test3AssignValueInVariableApproved");
String processInstanceID = processInstance.getId();
RuntimeService runtimeService = activitiRule.getRuntimeService();
Boolean Approved = (boolean)runtimeService.getVariable(processInstanceID, "Approved");
assertNotNull(Approved);
assertEquals(false, Approved);
}- TC4 test4WhenEmployeeNeedBudgetMoreThan1K - ทดสอบตาม Scenario ที่ได้กำหนดไว้
โดย Code สำหรับทดสอบสามารถเขียนได้ ดังนี้

@Test
public void test4WhenEmployeeNeedBudgetMoreThan1K()
{
System.out.println("Execute - test4WhenEmployeeNeedBudgetMoreThan1K");
ProcessDefinition definition = activitiRule.getRepositoryService().createProcessDefinitionQuery().processDefinitionKey("ApproveBudgetProcess").singleResult();
assertNotNull(definition);
/*
* ===============================================
* User Task : "Employee Request Budget"
* ===============================================
*/
TaskService taskService = activitiRule.getTaskService();
//https://community.alfresco.com/thread/217375-current-task-of-process
Task task =taskService.createTaskQuery().processInstanceId(processInstance.getId()).singleResult();
assertNotNull(task);
assertEquals("InputBudget", task.getTaskDefinitionKey());
System.out.println("InputBudget: " + task.getTaskDefinitionKey());
FormService formService = activitiRule.getFormService();
//List formList = formService.getStartFormData(definition.getId()).getFormProperties();
List formList = formService.getTaskFormData(task.getId()).getFormProperties();
assertEquals(1, formList.size());
System.out.println("Form Count: " + formList.size());
//ใส่ค่าลงใน Form
Map<String, String> formProperties = new HashMap<String, String>();
formProperties.put("budgetAmount", "10000.00");
//https://community.alfresco.com/thread/224087-exclusive-gateway-and-error-while-evaluating-expression-errors
formService.submitTaskFormData(task.getId(), formProperties);
/*
* ===============================================
* User Task : "Manager Review / Approve"
* ===============================================
*/
//Next Task รอ Approve
task =taskService.createTaskQuery().processInstanceId(processInstance.getId()).singleResult();
assertNotNull(task);
assertEquals("MangerReviewApproved", task.getTaskDefinitionKey());
//Manager OK
formList = formService.getTaskFormData(task.getId()).getFormProperties();
assertEquals(1, formList.size());
System.out.println("Form Count: " + formList.size());
formProperties = new HashMap<String, String>();
formProperties.put("Approved", "true");
formService.submitTaskFormData(task.getId(), formProperties);
/*
* ===============================================
* User Task : "Accountant Acknowledge"
* ===============================================
*/
task =taskService.createTaskQuery().processInstanceId(processInstance.getId()).singleResult();
assertNotNull(task);
assertEquals("AccountantAck", task.getTaskDefinitionKey());
}- TC5 test5WhenEmployeeNeedBudgetLessThanOrEqual1K - ทดสอบตาม Scenario ที่ได้กำหนดไว้ โดย Code สำหรับทดสอบสามารถเขียนได้ ดังนี้

@Test
public void test5WhenEmployeeNeedBudgetLessThanOrEqual1K()
{
System.out.println("Execute - test5WhenEmployeeNeedBudgetLessThanOrEqual1K");
ProcessDefinition definition = activitiRule.getRepositoryService().createProcessDefinitionQuery().processDefinitionKey("ApproveBudgetProcess").singleResult();
assertNotNull(definition);
/*
* ===============================================
* User Task : "Employee Request Budget"
* ===============================================
*/
TaskService taskService = activitiRule.getTaskService();
//https://community.alfresco.com/thread/217375-current-task-of-process
Task task =taskService.createTaskQuery().processInstanceId(processInstance.getId()).singleResult();
assertNotNull(task);
assertEquals("InputBudget", task.getTaskDefinitionKey());
System.out.println("InputBudget: " + task.getTaskDefinitionKey());
FormService formService = activitiRule.getFormService();
List formList = formService.getTaskFormData(task.getId()).getFormProperties();
assertEquals(1, formList.size());
System.out.println("Form Count: " + formList.size());
//ใส่ค่าลงใน Form
Map<String, String> formProperties = new HashMap<String, String>();
formProperties.put("budgetAmount", "992.50");
formService.submitTaskFormData(task.getId(), formProperties);
/*
* ===============================================
* User Task : "Accountant Acknowledge"
* ===============================================
*/
task =taskService.createTaskQuery().processInstanceId(processInstance.getId()).singleResult();
assertNotNull(task);
assertEquals("AccountantAck", task.getTaskDefinitionKey());
}Step 5 : ทดสอบ Run Unit Test
- คลิกขวาที่ UnitTest เลย ชื้อไฟล์ จากนั้น Run As >> JUnit ซึ่งทำเขียน Test ถูกต้อง ได้ผลลัพธ์ ดังนี้

ปัญหาที่เกิด
- ทดสอบ Run Test แล้วเจอ Error java.lang.NoClassDefFoundError สามารถแก้ไขโดยเพิ่ม JUnit ที่ Java Build Path ดังรูป

- ตอนนี้ตัว Activiti Framework ยังไม่รองรับ JUnit 5 นะครับ วิธีที่ผมแก้ มันแก้แบบลูกทุ่งไปก่อน เพราะ ตัว Class AcitiviRule มันช่วยทำให้ Test ได้ง่ายขึ้น
- ลำดับของการ Execute Test ของ JUnit มันมั่วมากครับ
Discover more from naiwaen@DebuggingSoft
Subscribe to get the latest posts sent to your email.


