Freigeben über


Komponententests dauerhafter Funktionen in Python

Komponententests sind ein wichtiger Bestandteil moderner Softwareentwicklungsmethoden. Komponententests überprüfen das Geschäftslogikverhalten und schützen sich vor der Einführung unbemerkter unmerkter Änderungen in der Zukunft. Dauerhafte Funktionen können leicht komplexer werden, sodass die Einführung von Komponententests dazu beiträgt, unterbrechungsfähige Änderungen zu vermeiden. In den folgenden Abschnitten wird erläutert, wie die drei Funktionstypen "Orchestration", "Orchestrator" und "Entitätsfunktionen" getestet werden.

Hinweis

Dieses Handbuch gilt nur für Apps mit dauerhaften Funktionen, die im Python v2-Programmiermodell geschrieben wurden.

Voraussetzungen

Die Beispiele in diesem Artikel erfordern Kenntnisse der folgenden Konzepte und Frameworks:

Einrichten der Testumgebung

Um dauerhafte Funktionen zu testen, ist es wichtig, eine richtige Testumgebung einzurichten. Dazu gehört das Erstellen eines Testverzeichnisses und das Installieren des Python-Moduls unittest in Ihrer Python-Umgebung. Weitere Informationen finden Sie in der Übersicht über die Azure Functions Python-Komponententests.

Komponententesttriggerfunktionen

Triggerfunktionen, die häufig als Clientfunktionen bezeichnet werden, initiieren Orchestrierungen und externe Ereignisse. So testen Sie diese Funktionen:

  • Simulieren Sie die DurableOrchestrationClient Orchestrierungsausführung und statusverwaltung.
  • Weisen Sie DurableOrchestrationClient Methoden wie start_new, oder get_statusraise_event mit simulierten Funktionen zu, die erwartete Werte zurückgeben.
  • Rufen Sie die Clientfunktion direkt mit einem simulierten Client und anderen erforderlichen Eingaben wie einem req (HTTP-Anforderungsobjekt) für HTTP-Triggerclientfunktionen auf.
  • Verwenden Sie Assertionen und unittest.mock Tools, um das erwartete Startverhalten, Parameter und HTTP-Antworten zu überprüfen.
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")

Komponententest-Orchestratorfunktionen

Orchestrator-Funktionen verwalten die Ausführung mehrerer Aktivitätsfunktionen. So testen Sie einen Orchestrator:

  • Mock the DurableOrchestrationContext to control function execution.
  • Ersetzen Sie DurableOrchestrationContext Methoden, die für die Orchestratorausführung wie call_activity oder create_timer durch simulierte Funktionen erforderlich sind. Diese Funktionen geben in der Regel Objekte vom Typ TaskBase mit einer result Eigenschaft zurück.
  • Rufen Sie den Orchestrator rekursiv auf, und übergeben Sie das Ergebnis der Aufgabe, die durch die vorherige Ertragsanweisung generiert wurde, an die nächste.
  • Überprüfen Sie das Orchestratorergebnis mithilfe der ergebnisse, die vom Orchestrator zurückgegeben wurden, und 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!"])

Komponententestentitätsfunktionen

Entitätsfunktionen verwalten zustandsbehaftete Objekte mit Vorgängen. So testen Sie eine Entitätsfunktion:

  • Simulieren Sie den DurableEntityContext internen Zustand und die Betriebseingaben der Entität.
  • Ersetzen Sie DurableEntityContext Methoden wie get_state, set_stateund operation_name durch Modelle, die kontrollierte Werte zurückgeben.
  • Rufen Sie die Entitätsfunktion direkt mit dem simulierten Kontext auf.
  • Verwenden Sie Assertionen, um Zustandsänderungen und zurückgegebene Werte zusammen mit unittest.mock Hilfsprogrammen zu überprüfen.
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)

Komponententestaktivitätsfunktionen

Aktivitätsfunktionen erfordern keine langlebigen Änderungen, die getestet werden müssen. Die Anleitung in der Übersicht über die Azure Functions Python-Komponententests reicht aus, um diese Funktionen zu testen.