단위 테스트는 최신 소프트웨어 개발 사례의 중요한 부분입니다. 단위 테스트는 비즈니스 논리 동작을 확인하고 향후 눈에 띄지 않는 호환성이 손상되는 변경을 도입하지 않도록 보호합니다. 지속성 함수는 복잡성이 쉽게 증가할 수 있으므로 단위 테스트를 도입하면 호환성이 손상되는 변경을 방지할 수 있습니다. 다음 섹션에서는 오케스트레이션 클라이언트, 오케스트레이터 및 엔터티 함수의 세 가지 함수 형식을 단위 테스트하는 방법을 설명합니다.
비고
이 가이드는 Python v2 프로그래밍 모델로 작성된 Durable Functions 앱에만 적용됩니다.
필수 조건
이 문서의 예제에는 다음 개념 및 프레임워크에 대한 지식이 필요합니다.
- 단위 테스트
- Durable Functions (듀러블 펑션)
- Python unittest
- unittest.mock
테스트 환경 설정
지속성 함수를 테스트하려면 적절한 테스트 환경을 설정하는 것이 중요합니다. 여기에는 테스트 디렉터리를 만들고 Python 환경에 Python 모 unittest
듈을 설치하는 것이 포함됩니다. 자세한 내용은 Azure Functions Python 단위 테스트 개요를 참조하세요.
트리거 함수 단위 테스트
클라이언트 함수라고도 하는 트리거 함수는 오케스트레이션 및 외부 이벤트를 시작합니다. 이러한 함수를 테스트하려면 다음을 수행합니다.
- 오케스트레이션 실행 및 상태 관리를 시뮬레이션하기 위해
DurableOrchestrationClient
를 모의합니다. -
DurableOrchestrationClient
,start_new
또는get_status
같은raise_event
메서드를 예상값을 반환하는 모의함수로 할당합니다. - 클라이언트 함수를 직접 호출할 때, 가상 클라이언트와
req
(HTTP 요청 객체) 같은 기타 필요한 입력을 사용하여 HTTP 트리거 클라이언트 함수를 실행합니다. - 어설션 및
unittest.mock
도구를 사용하여 예상되는 오케스트레이션 시작 동작, 매개 변수 및 HTTP 응답을 확인합니다.
import asyncio
import unittest
import azure.functions as func
from unittest.mock import AsyncMock, Mock, patch
from function_app import start_orchestrator
class TestFunction(unittest.TestCase):
@patch('azure.durable_functions.DurableOrchestrationClient')
def test_HttpStart(self, client):
# Get the original method definition as seen in the function_app.py file
func_call = http_start.build().get_user_function().client_function
req = func.HttpRequest(method='GET',
body=b'{}',
url='/api/my_second_function',
route_params={"functionName": "my_orchestrator"})
client.start_new = AsyncMock(return_value="instance_id")
client.create_check_status_response = Mock(return_value="check_status_response")
# Execute the function code
result = asyncio.run(func_call(req, client))
client.start_new.assert_called_once_with("my_orchestrator")
client.create_check_status_response.assert_called_once_with(req, "instance_id")
self.assertEqual(result, "check_status_response")
오케스트레이터 함수 단위 테스트
오케스트레이터 함수는 여러 작업 함수의 실행을 관리합니다. 오케스트레이터를 테스트하려면 다음을 수행합니다.
- 함수 실행을 제어하기 위해
DurableOrchestrationContext
를 모의 테스트합니다. -
DurableOrchestrationContext
오케스트레이터 실행에 필요한 메서드를call_activity
나create_timer
와 같은 모의 함수로 대체합니다. 이러한 함수는 일반적으로 속성이 있는 TaskBase 형식의 개체를result
반환합니다. - 오케스트레이터를 재귀적으로 호출하여 이전 yield 문에서 생성된 작업의 결과를 다음으로 전달합니다.
- 오케스트레이터와
unittest.mock
에서 반환된 결과를 사용하여 오케스트레이터 결과를 확인하십시오.
import unittest
from unittest.mock import Mock, patch, call
from datetime import timedelta
from azure.durable_functions.testing import orchestrator_generator_wrapper
from function_app import my_orchestrator
class TestFunction(unittest.TestCase):
@patch('azure.durable_functions.DurableOrchestrationContext')
def test_chaining_orchestrator(self, context):
# Get the original method definition as seen in the function_app.py file
func_call = my_orchestrator.build().get_user_function().orchestrator_function
# The mock_activity method is defined above with behavior specific to your app.
# It returns a TaskBase object with the result expected from the activity call.
context.call_activity = Mock(side_effect=mock_activity)
# Create a generator using the method and mocked context
user_orchestrator = func_call(context)
# Use orchestrator_generator_wrapper to get the values from the generator.
# Processes the orchestrator in a way that is equivalent to the Durable replay logic
values = [val for val in orchestrator_generator_wrapper(user_orchestrator)]
expected_activity_calls = [call('say_hello', 'Tokyo'),
call('say_hello', 'Seattle'),
call('say_hello', 'London')]
self.assertEqual(context.call_activity.call_count, 3)
self.assertEqual(context.call_activity.call_args_list, expected_activity_calls)
self.assertEqual(values[3], ["Hello Tokyo!", "Hello Seattle!", "Hello London!"])
엔터티 함수 단위 테스트
엔터티 함수는 작업을 사용하여 상태 저장 개체를 관리합니다. 엔터티 함수를 테스트하려면 다음을 수행합니다.
-
DurableEntityContext
을(를) 모킹하여 엔터티의 내부 상태와 작업 입력을 시뮬레이션합니다. -
DurableEntityContext
,get_state
,set_state
, 및operation_name
같은 메서드를 제어된 값을 반환하는 모의 메서드로 대체하십시오. - 모의 컨텍스트를 사용하여 엔터티 함수를 직접 호출합니다.
- 어설션을 사용하여
unittest.mock
유틸리티와 함께 상태 변경 및 반환된 값을 확인합니다.
import unittest
from unittest.mock import Mock, patch
from function_app import Counter
class TestEntityFunction(unittest.TestCase):
@patch('azure.durable_functions.DurableEntityContext')
def test_entity_add_operation(self, context_mock):
# Get the original method definition as seen in function_app.py
func_call = Counter.build().get_user_function().entity_function
# Setup mock context behavior
state = 0
result = None
def set_state(new_state):
nonlocal state
state = new_state
def set_result(new_result):
nonlocal result
result = new_result
context_mock.get_state = Mock(return_value=state)
context_mock.set_state = Mock(side_effect=set_state)
context_mock.operation_name = "add"
context_mock.get_input = Mock(return_value=5)
context_mock.set_result = Mock(side_effect=lambda x: set_result)
# Call the entity function with the mocked context
func_call(context_mock)
# Verify the state was updated correctly
context_mock.set_state.assert_called_once_with(5)
self.assertEqual(state, 5)
self.assertEqual(result, None)
단위 테스트 작업 함수
작업 함수는 지속성 관련 수정을 테스트할 필요가 없습니다. Azure Functions Python 단위 테스트 개요에 있는 지침은 이러한 함수를 테스트하기에 충분합니다.