What this is: A unittest.TestCase mixin for executing functional/integration
tests against containerized services. You configure the type of docker container
you wish to run as part of your tests, and the mixin's setUpClass()
method
handles starting the container, waiting for the container to be ready, and
storing the container's inspect
response data as part of the TestCase instance.
What this is not: A tool for testing your python app inside a docker container. There are other solutions/packages for that already.
I wanted a mechanism by which I could automate some functional tests involving a python LTI app (Django) and an external service, Instructure's Canvas LMS. LTI is a standard that allows external apps to be embedded within an LMS's website by means of an iframe. This makes it very difficult to do functional tests in isolation. So I created a docker image for spinning up a dev instance of Canvas and also this TestCase mixin for the purposes of automating it's execution during my tests. It's a pretty narrow use case, but the hope is that it can be useful for other types of containers.
pip install python-docker-test
To create a functional test that relies on a docker container you'll need to include a few additions to to your TestCase subclass:
-
Insert
PythonDockerTestMixin
at the beginning of your TestCase inheiritance chain. See here for why the position is important. -
Define at least the
CONTAINER_IMAGE
class attribute to specify the docker image you wish to run. If the specified image is not found in your local docker instance it will be pulled from the public registry. -
Optional but recommended, define a
container_ready_callback
class method. This method will be called from the thread that handles running the container. Within this method you should do things to confirm that whatever's running in your container is ready to run whatever your tests are exercising. The method should simply return if everything is all set, otherwise raise apython_docker_test.ContainerNotReady
. The method will be called with a positional argument of a dict structure containing the result of docker-py'sinspect_container
, so you can know things like the container's ip and gateway address. -
Optionally set the
CONTAINER_READY_TRIES
andCONTAINER_READY_SLEEP
class attributes to control how many times yourcontainer_ready_callback
method is called before giving up, and how long the thread will sleep between calls.
import unittest
from python_docker_test import PythonDockerTestMixin
class MyTests(PythonDockerTestMixin, unittest.TestCase):
CONTAINER_IMAGE = 'foobar/my_image'
CONTAINER_READY_TRIES = 3
CONTAINER_READY_SLEEP = 10
@classmethod
def container_ready_callback(cls, container_data):
try:
# request to base url should redirect to login when ready
container_ip = container_data['NetworkSettings']['IPAddress']
resp = requests.head('http://{}:3000'.format(container_ip))
assert resp.status_code == 302
return
except (requests.ConnectionError, AssertionError):
raise ContainerNotReady()
def test_something(self):
# container should be running; test some stuff!
...
If, like me, you need the service(s) in the container to communicate back to the
app under test there is an additional hurdle of making the app accessible from
within the container. The simplest approach that I found is to bind the app to
the container's gateway IP. By default, using docker's "bridge" networking mode,
the gateway address is 172.17.42.1
. So, assuming a Django app, prior to
executing the functional tests you'll need to start an instance of your app like
so:
python manage.py runserver 172.17.42.1:8000
You should then be able to access the app at that address from within the container.
"What if I want to automate starting the app too?" Well, in the case of Django
you can use LiveServerTestCase
. The trick there is overriding the default
ip:port the server is bound to. In my case I set
os.environ['DJANGO_LIVE_TEST_SERVER_ADDRESS']
in my ready callback method
based on info in the container inspection data.
- Jay Luker <[email protected]> @lbjay
- Matthieu Chevrier @treemo
Apache 2.0
2015 President and Fellows of Harvard College