Skip to content

Unit Testing Guide

Hanzhang Zeng edited this page Mar 24, 2021 · 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 --index-url https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple -U -e .[dev] to install the dependencies
  3. Run python setup.py build to generate grpc packages
  4. Run python setup.py webhost to download the WEBHOST defined in setup.py
  5. Run python setup.py 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