Python extras#
Overview#
These notes cover useful python skills, particularly for finalizing your team’s project.
Logging#
These notes were adapted from [@pythonLoggingPythonReal]
See this article from Real Python for a complete explanation.
Important notes:
Unit testing#
These notes were adapted from:
[@pythonEffectivePythonTesting]
[@PytestDocumentation]
There are two main libraries for unit tests in python:
unittest: included in the standard python library
pytest: a very popular third party library for writing unit tests in python
I’ve used both before, and they’re both great. In this course, we’ll be using pytest.
There’s a few reasons for that, which mostly aren’t that important, but in general,
compared to unittest, pytest requires far less boilerplate code to accomplish the same things.
For example:
To create one unit test that always passes, and one that always fails, using unittest:
from unittest import TestCase
class TryTesting(TestCase):
def test_always_passes(self):
self.assertTrue(True)
def test_always_fails(self):
self.assertTrue(False)
The following steps were necessary:
Import the
TestCaseclass fromunittestCreate
TryTesting, a subclass ofTestCaseWrite a method in
TryTestingfor each testUse one of the
self.assertmethods fromunittest.TestCaseto make assertions
This boilerplate code is required for every single test.
To create one unit test that always passes, and one that always fails, using pytest:
def test_always_passes():
assert True
def test_always_fails():
assert False
The following steps were necessary:
define functions beginning with the name
test_use Python’s built-in
assertkeyword directly
Much less boilerplate code required!
Installing pytest#
In your virtual environment,
use pip to install pytest:
$ pip install pytest
Creating and running tests#
Create a new file called test_sample.py, containing a function, and a test:
def func(x):
return x + 1
def test_answer():
assert func(3) == 5
Run the test:
$ pytest
See pytest: Getting Started documentation for more details.
See my example_system unit tests for examles relevant to our project.
Dependency injection with pytest fixutres#
pytest is designed to follow a variant of the “functional” testing paradigm called Arrange-Act-Assert:
Arrange, or set up, the conditions for the test
Act by calling some function or method
Assert that some end condition is true
pytest enables the creation of fixtures, that is, test objects, that can be used across tests
to simplify the Arrange step:
import pytest
# This annotation marks the function as a fixture generator
@pytest.fixture
def example_fixture():
return 1
# Make tests use that fixture by providing the name as a parameter of the test function:
def test_with_fixture(example_fixture):
assert example_fixture == 1
You can see how I use fixtures in the example_system conftest.py files of the sample test code in the final-project-upstream.
conftest.py#
You’ll notice in the example_system tests
that I define fixtures in files named conftest.py:
#| source-line-numbers: "2,4"
final-project-upstream/iot_subsystems/tests/example_system/
├── conftest.py
├── integration
│ ├── conftest.py
│ ├── __init__.py
│ └── test_system.py
└── unit
├── __init__.py
├── test_aht20.py
└── test_fan.py
This is a pytest convention for sharing fixtures across multiple test files, allowing you to keep your test files concise and to avoid needing to recreate dependencies. This is very useful for our project, where we have many custom classes we are creating and mocking.
You can see more about conftest.py in the
pytest documentation
using pytest on asynchronous code#
In our final project, there are quite a few asynchronous functions to test.
See the example_system.py unit tests from the final-project-upstream code for an example.
using logot to test logs#
pytest does include a fixture called caplog for testing if logs were written.
I found that the 3rd party package logot was much simpler to use.
See the example_system.py unit tests from the final-project-upstream code for an example:
Further pytest reference#
I found the following documentation really useful when I was making the example_system/ starter code tests: