Python FDK - Unit test your function!

Denis Makogon
Fn Project
Published in
4 min readSep 12, 2018

At the beginning, Fn platform offered several officially supported FDKs. As time passed, more FDKs were introduced and supported by the Fn CLI.

The goal of an FDK is pretty straightforward — hide Fn-to-function protocol intercommunication along with providing a simple to use programming interface. The goal is to make the UX the same no matter which programming language a developer picks to write serverless functions.

Testing

Fn CLI (see fn test) provides the first barrier of the serverless function testing by creating a configurable interface for executing black-box testing. Let’s take a look at test.json :

{
"tests": [
{
"input": {
"body": {
"name": "Johnny"
}
},
"output": {
"body": {
"message": "Hello Johnny"
}
}
},
{
"input": {
"body": ""
},
"output": {
"body": {
"message": "Hello World"
}
}
}
]
}

As you’ve noticed, test.json contains a list of black-box tests where a developer can define the input and the expected output of a function. So, running this type of test gives a first impression of how a function acts (is it stable, safe to use, etc.).

What is this type of testing really all about? fn testprovides two modes for running these tests: local and remote. The key difference is, for a local mode you need to have Docker up and running. The procedure is:

  • do docker build
  • do docker run
  • ship protocol-framed requests to a function
  • tear down a function

While using fn test in a remote mode, the Fn CLI attempts to communicate with a remote Fn server in order to run a function on a real deployment rather than on an Fn CLI, sugaring on top of the Docker CLI. So, fn testprovides a first impression of the overall function lifecycle.

There are a few reasons why we don’t really like fn test including the fact that it involves Docker, i.e. a developer needs to build an image and run it for a very simple black-box test.

Unit testing in FDK

Protocol request/response processing is a mission-critical feature of each FDK, which is why officially supported FDKs are well tested and 100% upstream Fn-feature-compliant.

Despite certain minor differences, FDKs are the same at their core. However, certain FDKs provide programming language-specific features like promises in FDK-Node and async/await in FDK-Python. One of the key features of an officially supported FDK is that it is in consideration for introduction is unit testing. At this particular moment, only the Java FDK provides the necessary API to write unit tests (JUnit) in the most efficient way. We hope to provide the same feature coverage with additional FDKs soon.

Starting with v0.0.33, Python FDK will provide an extensive framework for running unit tests. Let’s take a look at the test suite for a Python FDK serverless function:

As you may have noticed, the idea is to provide a function boilerplate along with native unit tests that cover the particular implementation (tests would remain stable as long as functions implementation remains the same). If you take a look at the test suites, you’d see the following assertion methods:

assertInHeaders - allows asserting header(s) presence in responseassertInTime - allows asserting the time necessary for a function to finishassertNotInTime - allows asserting the time within a function was not able to finishassertResponseConsistent - allows to check the response data, if the data corresponds to assertion expectations

If you see a need to add extra assertion methods, feel free to open an issue with the detailed description.

How to run tests?

If you are familiar with native testing tools — just use PyTest. If not, no worries, all you need is to install pytest tool and run the following command:

pytest -v -s --tb=long func.py

func.py is where your functions code file or the place where your function test suites live.

With that command you’d get the following output:

With some additional plugins to the pytest you may get the test coverage. Use the following command to install the coverage plugin:

pip install pytest-cov

After this, all you need to do is to add one more CLI flag to the pytest command:

pytest -v -s --tb=long --cov=func func.py

Basically, you are telling to the pytest to calculate coverage for your func.py module, and the output will be:

You may find the following links useful as a general follow-up on this post:

Finally, join the Fn Army on Slack!

--

--