Skip to content

Unit Testing Guide

gavin-aguiar edited this page Jul 23, 2024 · 13 revisions

Prerequisites

  1. Create a virtual python environment under azure-functions-python-worker project directory (supports Python 3.6/3.7/3.8)
  2. Run python -m pip install -U -e .[dev] to install the dependencies
  3. Run cd tests && invoke -c test_setup build-protos to generate grpc packages
  4. Run invoke -c test_setup webhost to download the WEBHOST defined in setup.py
  5. Run invoke -c test_setup extension to download the extension defined in setup.py
  6. Test a case with pytest ./tests/unittests/test_code_quality.py::TestCodeQuality::test_mypy

Python functions for Azure can be tested like regular Python code using standard testing frameworks. For most bindings it is possible to create a mock input object by creating an instance of an appropriate class from the azure.functions package.

For example, below is a mock test of an HttpTrigger function:

myapp/__init__.py:

import azure.functions as func

def my_function(req: func.HttpRequest) -> func.HttpResponse:
    name = req.params.get('name')
    if not name:
        name = 'Incognito'
    return func.HttpResponse(f"Hello {name}!")

myapp/test_func.py:

import unittest

import azure.functions as func
from . import my_function

class TestFunction(unittest.TestCase):
    def test_my_function(self):
        # Construct a mock HTTP request.
        req = func.HttpRequest(
            method='GET',
            body=None,
            url='/my_function', 
            params={'name': 'Test'})

        # Call the function.
        resp = my_function(req)

        # Check the output.
        self.assertEqual(
            resp.get_body(), 
            'Hello, Test!',
        )

Another example, with a queue trigger function:

queueapp/__init__.py:

import azure.functions as func

def my_function(msg: func.QueueMessage) -> str:
    return f'msg body: {msg.get_body().decode()}'

queueapp/test_func.py:

import unittest

import azure.functions as func
from . import my_function

class TestFunction(unittest.TestCase):
    def test_my_function(self):
        # Construct a mock Queue message.
        req = func.QueueMessage(
            body=b'test')

        # Call the function.
        resp = my_function(req)

        # Check the output.
        self.assertEqual(
            resp, 
            'msg body: test',
        )

Troubleshooting

  1. error : Metadata generation failed while doing python setup.py webhost (on Mac OS) - azure-functions-host/5229, azure-functions-host/4055

Please try changing the Version of ExtensionsMetadataGenerator to 1.1.2 (ref) and try the build again. You can directly go inside [azure-functions-python-worker]/build/extensions folder and change the extensions.csproj file.

<PackageReference
    Include="Microsoft.Azure.WebJobs.Script.ExtensionsMetadataGenerator"
    Version="1.1.2"
/>

and then run the following command

dotnet build -o bin
  1. (a). If you hit the following error even after changing the ExtensionsMetadataGenerator version:
/Users/varadmeru/.nuget/packages/microsoft.azure.webjobs.script.extensionsmetadatagenerator/1.1.2/build/Microsoft.Azure.WebJobs.Script.ExtensionsMetadataGenerator.targets(33,5): error : Metadata generation failed. Exit code: '150' Error: 'It was not possible to find any compatible framework versionThe framework 'Microsoft.NETCore.App', version '2.0.0' was not found.
- The following frameworks were found:      3.1.1 at [/usr/local/share/dotnet/shared/Microsoft.NETCore.App]You can resolve the problem by installing the specified framework and/or SDK.The specified framework can be found at:  
- https://aka.ms/dotnet-core-applaunch?framework=Microsoft.NETCore.App&framework_version=2.0.0&arch=x64&rid=osx.10.15-x64' [/Users/varadmeru/work/microsoft/azfunctions/pyworker/build/extensions/extensions.csproj]

Please install older dotnet version using the dotnet-install scripts (.NET Core CLI) and try the above step again.

➜ ./dotnet-install.sh                              # Installs 3.1 (Current latest)
➜ ./dotnet-install.sh -version 2.1.2 --verbose     # Installs 2.1
➜ ./dotnet —list-sdks                              # list all the installations along with the path to the SDK.
2.1.2 [/path/.dotnet/sdk]
3.1.201 [/path/.dotnet/sdk]

When tried with the new SDK and updated ExtensionsMetadataGenerator's version to 1.1.2, the following command should work.

➜ venv/bin/python setup.py webhost
running webhost
Downloading Azure Functions Web Host...
Extracting Azure Functions Web Host binaries...
Microsoft (R) Build Engine version 16.5.0+d4cbfca49 for .NET Core
Copyright (C) Microsoft Corporation. All rights reserved.

  Restore completed in 2.42 sec for /Users/hack/azfunctions/pyworker/build/extensions/extensions.csproj.
  extensions -> /Users/hack/azfunctions/pyworker/build/extensions/bin/extensions.dll

Build succeeded.
    0 Warning(s)
    0 Error(s)

Time Elapsed 00:00:10.83
  1. If you hit module named 'azure' error when running the tests (eg: pytest ./tests/unittests/test_code_quality.py)

An example stacktrace:

../../venv/lib/python3.7/site-packages/_pytest/python.py:513: in _importtestmodule
    mod = self.fspath.pyimport(ensuresyspath=importmode)
../../venv/lib/python3.7/site-packages/py/_path/local.py:701: in pyimport
    __import__(modname)
test_types.py:3: in <module>
    from azure import functions as azf
E   ModuleNotFoundError: No module named 'azure'

Then please clone the AzFunctions Python Library and from the python binary of the virtual environment (created at step 1), install the functions library - venv/bin/python -m pip install -e <path to functions lib>/azure-functions-python-library/

  1. Could not start the webworker:
test setup failed
Traceback (most recent call last):
  File "/Users/varadmeru/work/microsoft/azfunctions/pyworker/azure_functions_worker/testutils.py", line 190, in setUpClass
    stdout=cls.host_stdout)
  File "/Users/varadmeru/work/microsoft/azfunctions/pyworker/azure_functions_worker/testutils.py", line 693, in start_webhost
    raise RuntimeError('could not start the webworker')
RuntimeError: could not start the webworker