หลงจาก Blog ตอนที่แล้ว ที่ได้สร้าง Process ของการขอเบิกเงินไปแล้ว ตอนนี้เราต้องมา Proof ก่อนว่า Process ที่ทำนั้น มันใช้งานได้จริงๆ พร้อมที่นำไป Deploy ขึ้น Activiti Server ครับ โดยมีขั้นตอน ดังนี้
Step 1 : กำหนดโครงสร้างไฟล์ที่เกี่ยวข้องกันก่อน
<Project Name> |-src |-main | |-java [1] | |-resources [2] | |-diagrams | |-Test.bpmn |-test |-java [3] | |-ProcessTestApproveBugetProcess.java |-resources [4] |-Test.bpmn |-activiti.cfg.xml
Step 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(); } }
- @BeforeEach - ในที่นี้เป็นการ Deploy Process ที่เพิ่งสร้าง ให้ Activiti-Engine รู้จัก
- ส่วน @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()); }
- TC1 test1ProcessIsStart - บอกว่า Process ที่ Deploy ไปพร้อมใช้งาน หรือไม่ ?
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.