support bpmn unit test method mocks (#1146)

Co-authored-by: burnettk <burnettk@users.noreply.github.com>
This commit is contained in:
Kevin Burnett 2024-03-03 06:50:38 -08:00 committed by GitHub
parent 222685155a
commit e584d47205
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 104 additions and 21 deletions

View File

@ -362,15 +362,12 @@ class CustomBpmnScriptEngine(PythonScriptEngine): # type: ignore
task: SpiffTask | None = None,
external_context: dict[str, Any] | None = None,
) -> Any:
"""Evaluate the given expression, within the context of the given task and return the result."""
methods = self.__get_augment_methods(task)
if external_context:
methods.update(external_context)
if hasattr(self, "method_overrides"):
if self.method_overrides:
methods = {**methods, **self.method_overrides}
"""Evaluate the given expression, within the context of the given task and return the result."""
try:
return super()._evaluate(expression, context, external_context=methods)
except Exception as exception:
@ -392,10 +389,6 @@ class CustomBpmnScriptEngine(PythonScriptEngine): # type: ignore
if external_context:
methods.update(external_context)
if hasattr(self, "method_overrides"):
if self.method_overrides:
methods = {**methods, **self.method_overrides}
# do not run script if it is blank
if script:
super().execute(task, script, methods)

View File

@ -43,17 +43,39 @@ class BpmnFileMissingExecutableProcessError(Exception):
class ProcessModelTestRunnerScriptEngine(PythonScriptEngine): # type: ignore
def execute(self, task: SpiffTask, script: str, external_context: Any = None) -> bool:
def __init__(self, method_overrides: dict | None = None) -> None:
self.method_overrides = method_overrides
super().__init__()
def _get_all_methods_for_context(self, external_context: dict[str, Any] | None) -> dict:
methods = {
"get_process_initiator_user": lambda: {
"username": "test_username_a",
"tenant_specific_field_1": "test_tenant_specific_field_1_a",
},
}
if self.method_overrides:
methods = {**methods, **self.method_overrides}
if external_context:
methods.update(external_context)
return methods
def evaluate(self, task: SpiffTask, expression: str, external_context: dict[str, Any] | None = None) -> Any:
"""
Evaluate the given expression, within the context of the given task and
return the result.
"""
updated_context = self._get_all_methods_for_context(external_context)
return super().evaluate(task, expression, updated_context)
def execute(self, task: SpiffTask, script: str, external_context: Any = None) -> bool:
if script:
methods = self._get_all_methods_for_context(external_context)
super().execute(task, script, methods)
return True
def call_service(
@ -146,17 +168,6 @@ class ProcessModelTestRunnerMostlyPureSpiffDelegate(ProcessModelTestRunnerDelega
bpmn_process_spec,
subprocess_specs=subprocesses,
)
bpmn_process_instance.script_engine = ProcessModelTestRunnerScriptEngine()
# we do not want to call the real get_process_initiator_user script, since it depends on a process instance
# that does not actually exist
overridden_methods = {
"get_process_initiator_user": lambda: {
"username": "test_username_a",
"tenant_specific_field_1": "test_tenant_specific_field_1_a",
},
}
bpmn_process_instance.script_engine.method_overrides = overridden_methods
return bpmn_process_instance
def execute_task(self, spiff_task: SpiffTask, task_data_for_submit: dict | None = None) -> None:
@ -339,6 +350,11 @@ class ProcessModelTestRunner:
def run_test_case(self, bpmn_file: str, test_case_identifier: str, test_case_contents: dict) -> None:
bpmn_process_instance = self._instantiate_executer(bpmn_file)
method_overrides = {}
if "mocks" in test_case_contents:
for method_name, mock_return_value in test_case_contents["mocks"].items():
method_overrides[method_name] = lambda value=mock_return_value: value
bpmn_process_instance.script_engine = ProcessModelTestRunnerScriptEngine(method_overrides=method_overrides)
next_task = self._get_next_task(bpmn_process_instance)
while next_task is not None:
test_case_task_properties = None

View File

@ -0,0 +1,9 @@
{
"description": "",
"display_name": "Script Task",
"exception_notification_addresses": [],
"fault_or_suspend_on_exception": "fault",
"metadata_extraction_paths": null,
"primary_file_name": "script_task.bpmn",
"primary_process_id": "Process_Script_Task"
}

View File

@ -0,0 +1,41 @@
<?xml version="1.0" encoding="UTF-8"?>
<bpmn:definitions xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" id="Definitions_96f6665" targetNamespace="http://bpmn.io/schema/bpmn" exporter="Camunda Modeler" exporterVersion="3.0.0-dev">
<bpmn:process id="Process_Script_Task" isExecutable="true">
<bpmn:startEvent id="StartEvent_1">
<bpmn:outgoing>Flow_0qfycuk</bpmn:outgoing>
</bpmn:startEvent>
<bpmn:sequenceFlow id="Flow_0qfycuk" sourceRef="StartEvent_1" targetRef="Activity_1qdbp6x" />
<bpmn:endEvent id="Event_1kumwb5">
<bpmn:incoming>Flow_1auiekw</bpmn:incoming>
</bpmn:endEvent>
<bpmn:sequenceFlow id="Flow_1auiekw" sourceRef="Activity_1qdbp6x" targetRef="Event_1kumwb5" />
<bpmn:scriptTask id="Activity_1qdbp6x" name="Script">
<bpmn:incoming>Flow_0qfycuk</bpmn:incoming>
<bpmn:outgoing>Flow_1auiekw</bpmn:outgoing>
<bpmn:script>a = 1
frontend_url_for_testing = get_frontend_url()
</bpmn:script>
</bpmn:scriptTask>
</bpmn:process>
<bpmndi:BPMNDiagram id="BPMNDiagram_1">
<bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Process_Script_Task">
<bpmndi:BPMNShape id="_BPMNShape_StartEvent_2" bpmnElement="StartEvent_1">
<dc:Bounds x="179" y="159" width="36" height="36" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Event_1kumwb5_di" bpmnElement="Event_1kumwb5">
<dc:Bounds x="432" y="159" width="36" height="36" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Activity_0ii0b3p_di" bpmnElement="Activity_1qdbp6x">
<dc:Bounds x="270" y="137" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge id="Flow_0qfycuk_di" bpmnElement="Flow_0qfycuk">
<di:waypoint x="215" y="177" />
<di:waypoint x="270" y="177" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_1auiekw_di" bpmnElement="Flow_1auiekw">
<di:waypoint x="370" y="177" />
<di:waypoint x="432" y="177" />
</bpmndi:BPMNEdge>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</bpmn:definitions>

View File

@ -0,0 +1,24 @@
{
"test_case_1": {
"mocks": {
"get_frontend_url": "https://spiffworkflow.example.com",
"get_process_initiator_user": {
"username": "test_username_a",
"tenant_specific_field_1": "test_tenant_specific_field_1_a"
}
},
"tasks": {
"Activity_1vepcwc": {
"data": [
{
"what": true
}
]
}
},
"expected_output_json": {
"a": 1,
"frontend_url_for_testing": "https://spiffworkflow.example.com"
}
}
}