-
Notifications
You must be signed in to change notification settings - Fork 57
Add change supporting unit testing #537
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 17 commits
Commits
Show all changes
29 commits
Select commit
Hold shift + click to select a range
0e33ce3
Add change supporting unit testing
andystaples 90500a2
Add support for durable client functions
andystaples ed470ce
Naming
andystaples 69bd129
Add test samples to fan_in_fan_out app
andystaples a1282eb
Linting fixes
andystaples 5adfb63
Merge branch 'dev' into andystaples/add-unit-testing-change
andystaples 11cad72
Linting fixes 2
andystaples 6d8c330
Merge branch 'andystaples/add-unit-testing-change' of https://github.…
andystaples 2ef009f
Linter fixes 3
andystaples 4584c90
Probable test issue fix
andystaples 8f09b5a
Exclude samples from pytest github workflow
andystaples 7902b65
Add testing matrix for samples
andystaples d2f8d1e
Pipeline fix
andystaples 55886db
Build extension into tests
andystaples a2ca265
Merge branch 'dev' into andystaples/add-unit-testing-change
andystaples a9dd0e0
Add tests to other projects
andystaples 7e10d5e
Tweak script
andystaples 545fbcc
Update tests from PR feedback
andystaples c5f3540
Fix tests
andystaples f2db1cd
Fix tests
andystaples 3a2f94b
Fix tests
andystaples 0cb7871
PR feedback
andystaples 81b67f5
Expose OrchestratorGeneratorWrapper in SDK (#548)
andystaples 02ab139
Linting fixes
andystaples 739a9e8
Test fix
andystaples b489e05
Linting fix
andystaples 2343381
More linting and test fixes
andystaples 850b2d0
Linting again
andystaples 9ab1c17
Delete azure-functions-durable-python.sln
andystaples File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
from datetime import timedelta | ||
import unittest | ||
from unittest.mock import Mock, call, patch | ||
|
||
from durable_blueprints import my_orchestrator | ||
|
||
# A way to wrap an orchestrator generator to simplify calling it and getting the results. | ||
# Because orchestrators in Durable Functions always accept the result of the previous activity for the next send() call, | ||
# we can simplify the orchestrator like this to also simplify per-test code. | ||
def orchestrator_generator_wrapper(generator): | ||
previous = next(generator) | ||
yield previous | ||
while True: | ||
try: | ||
previous_result = None | ||
try: | ||
previous_result = previous.result | ||
except Exception as e: # Simulated activity exceptions, timer interrupted exceptions, anytime a task would throw. | ||
previous = generator.throw(e) | ||
else: | ||
previous = generator.send(previous_result) | ||
yield previous | ||
except StopIteration as e: | ||
yield e.value | ||
return | ||
|
||
|
||
class MockTask(): | ||
def __init__(self, result=None): | ||
self.result = result | ||
|
||
|
||
def mock_activity(activity_name, input): | ||
if activity_name == "say_hello": | ||
return MockTask(f"Hello {input}!") | ||
raise Exception("Activity not found") | ||
|
||
|
||
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 | ||
|
||
context.call_activity = Mock(side_effect=mock_activity) | ||
# Create a generator using the method and mocked context | ||
user_orchestrator = func_call(context) | ||
|
||
# Use a method defined above to get the values from the generator. Quick unwrap for easy access | ||
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!"]) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,4 +4,5 @@ | |
|
||
azure-functions | ||
azure-functions-durable | ||
azure-storage-blob | ||
azure-storage-blob | ||
pytest |
63 changes: 63 additions & 0 deletions
63
samples-v2/fan_in_fan_out/tests/test_E2_BackupSiteContent.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
import unittest | ||
from unittest.mock import Mock, call, patch | ||
|
||
from function_app import E2_BackupSiteContent | ||
|
||
# A way to wrap an orchestrator generator to simplify calling it and getting the results. | ||
# Because orchestrators in Durable Functions always accept the result of the previous activity for the next send() call, | ||
# we can simplify the orchestrator like this to also simplify per-test code. | ||
def orchestrator_generator_wrapper(generator): | ||
previous = next(generator) | ||
yield previous | ||
while True: | ||
try: | ||
previous_result = None | ||
try: | ||
previous_result = previous.result | ||
except Exception as e: # Simulated activity exceptions, timer interrupted exceptions, anytime a task would throw. | ||
previous = generator.throw(e) | ||
else: | ||
previous = generator.send(previous_result) | ||
yield previous | ||
except StopIteration as e: | ||
yield e.value | ||
return | ||
|
||
|
||
class MockTask(): | ||
def __init__(self, result=None): | ||
self.result = result | ||
|
||
|
||
def mock_activity(activity_name, input): | ||
if activity_name == "E2_GetFileList": | ||
return MockTask(["C:/test/E2_Activity.py", "C:/test/E2_Orchestrator.py"]) | ||
return MockTask(input) | ||
|
||
|
||
class TestFunction(unittest.TestCase): | ||
@patch('azure.durable_functions.DurableOrchestrationContext') | ||
def test_E2_BackupSiteContent(self, context): | ||
# Get the original method definition as seen in the function_app.py file | ||
func_call = E2_BackupSiteContent.build().get_user_function().orchestrator_function | ||
|
||
context.get_input = Mock(return_value="C:/test") | ||
context.call_activity = Mock(side_effect=mock_activity) | ||
context.task_all = Mock(return_value=MockTask([100, 200, 300])) | ||
|
||
# Create a generator using the method and mocked context | ||
user_orchestrator = func_call(context) | ||
|
||
# Use a method defined above to get the values from the generator. Quick unwrap for easy access | ||
values = [val for val in orchestrator_generator_wrapper(user_orchestrator)] | ||
|
||
expected_activity_calls = [call('E2_GetFileList', 'C:/test'), | ||
call('E2_CopyFileToBlob', 'C:/test/E2_Activity.py'), | ||
call('E2_CopyFileToBlob', 'C:/test/E2_Orchestrator.py')] | ||
|
||
self.assertEqual(context.call_activity.call_count, 3) | ||
self.assertEqual(context.call_activity.call_args_list, expected_activity_calls) | ||
|
||
context.task_all.assert_called_once() | ||
# Sums the result of task_all | ||
self.assertEqual(values[2], 600) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
# Stub file - test this function with standard Azure Functions Python testing tools. | ||
bachuv marked this conversation as resolved.
Show resolved
Hide resolved
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
# Stub file - test this function with standard Azure Functions Python testing tools. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
import asyncio | ||
import unittest | ||
import azure.functions as func | ||
from unittest.mock import AsyncMock, Mock, patch | ||
|
||
from function_app import HttpStart | ||
|
||
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 = chaining_orchestrator.build().get_user_function_unmodified() | ||
bachuv marked this conversation as resolved.
Show resolved
Hide resolved
|
||
func_call = HttpStart.build().get_user_function().client_function | ||
|
||
req = func.HttpRequest(method='GET', | ||
body=b'{}', | ||
url='/api/my_second_function', | ||
route_params={"functionName": "E2_BackupSiteContent"}) | ||
|
||
client.start_new = AsyncMock(return_value="instance_id") | ||
client.create_check_status_response = Mock(return_value="check_status_response") | ||
|
||
# Create a generator using the method and mocked context | ||
result = asyncio.run(func_call(req, client)) | ||
|
||
client.start_new.assert_called_once_with("E2_BackupSiteContent", client_input={}) | ||
client.create_check_status_response.assert_called_once_with(req, "instance_id") | ||
self.assertEqual(result, "check_status_response") |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,3 +4,4 @@ | |
|
||
azure-functions | ||
azure-functions-durable | ||
pytest |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.