Примечание
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Модульное тестирование является важной частью современных методик разработки программного обеспечения. Модульные тесты проверяют поведение бизнес-логики и защищают от внедрения незамеченных критических изменений в будущем. Устойчивые функции могут легко нарастать в сложности, поэтому внедрение модульных тестов помогает избежать нарушающих изменений. В следующих разделах объясняется, как выполнять модульное тестирование трех типов функций: клиента оркестрации, оркестратора и функций сущности.
Замечание
Это руководство относится только к приложениям устойчивых функций, написанным в модели программирования Python версии 2.
Предпосылки
В примерах этой статьи требуются знания о следующих понятиях и платформах:
- Модульное тестирование
- Устойчивые функции
- Модульный тест Python
- unittest.mock
Настройка тестовой среды
Чтобы протестировать устойчивые функции, важно настроить правильную тестовую среду. Это включает создание тестового каталога и установку модуля Python unittest
в среду Python. Дополнительные сведения см. в обзоре модульного тестирования функций 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
, макетами, возвращающими управляемые значения. - Вызовите функцию сущности непосредственно с помощью макетированного контекста.
- Используйте утверждения (assertions) для проверки изменений состояния и возвращаемых значений совместно с
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)
Функции модульного тестирования в деятельности
Функции активности не требуют специфических для Durable модификаций для тестирования. Рекомендации, приведенные в обзоре модульного тестирования функций Python, достаточно для тестирования этих функций.