Introduction
Automation has revolutionized the world in many ways and none more obvious than in testing our code. Automated testing liberates us in development and deployment in a way that is quite remarkable. An experienced development team can now release features in weeks not months.
The general umbrella for this is referred to as Continuous Integration (CI) and Continuous Deployment (CD). My experience with using these various services (like gitlab or github ci) left me wanting a faster feedback loop.
Motivation
CI/CD is a very useful feedback loop when it comes to collaborating with others; so why not tighten this loop up for rapid iterative development. Testing is about feedback. The faster you get this feedback the better.
Intended Audience
- Anyone with a test suite
- Anyone who wants to mimic the CI/CD feedback loop
- Try hard coders trying to save precious keystrokes
- System Admins looking for ways to get their developers TO RUN THEIR OWN FREAKING TESTS.
- NOT really for people who code in compiled languages - still useful to run functional tests
Technology
- Docker - All of my development occurs in Docker. I do this for a number of reasons that are beyond the scope of this tutorial. The bullet points are;
- Isolate dependencies from system
- Reproducible builds
- OS agnostic development
- entr - Run arbitrary commands when files change.
- ag (silver searcher) - SUPER fast recursive search that ignores git.
Setup (aka running Python Workflow Image)
Most of effort here for me was conceiving of how this will all work as well as finding the right set of tools… and learning them.
It’s pretty minimal effort to take advantage of these techniques using my personal docker/python dev image, derekadair/python-workflow:dev. It is the official python image with entr/ag and some other goodies installed. While you can use entr/ag/docker to do this with any language - i will be assuming the use of my dev image going forward.
Create and run a container
Run the following magical incantation at the root of any python project to start a container that is; 1) named my_test_runner 2) has a volume linked to your current directory to the /code directory on the container 3) boots into an interactive bash shell 4) Detached with a sudo TTY
docker run -dt --name my_test_runner -v $(pwd):/code derekadair/python-workflow:dev bash -il
Install your dependencies
From your running containers terminal - you only have to do this once while you maintain the container and volume.
docker exec my_test_runner pip install -r requirements.txt
Run your tests on file change
docker exec -it my_test_runner bash -c 'ag -l | entr -dc YOUR_COMMAND_TO_RUN_TESTS'
Command Breakdown
Pretty simple… for YOU. I experimented with various tools, setups, test runners, test frameworks, docker images. This is the most elegant, fast and lightweight solution I came across which can be broken down into two parts that install on most unix systems;
1) Send the commands to the container via a bash shell
docker exec -it my_test_runner bash -c
docker exec -it my_test_runner - execute a command with psuedo TTY
bash -c - send a command to bash
2) Intelligently search for files
ag -l
is how you list all of the files recurisively that are not in git. You can check out the documentation for ag by typing ag
in your containers terminal. -l without any arguments simply lists everything thats not in .git and is smart, in general, about what files to ignore.
3) pipe your file lists to entr
Piping a file list to entr will tell it to watch every file in the list, pass the list and execute your test command(s).
| entr -dc YOUR_COMMAND_TO_RUN_TESTS
-d - this flag watches for new files
-c - this flag clears the screen when a file changes
Extras
Docker exec to run multiple test loops
I like to run my unit tests (quick) next to my functional tests (long). You can execute the command the command over and over again from a seperate shell. I’ve not really pushed this to the limit so it may be better to create multiple containers at some point.
Use -L for an ignore list
Running ag
will show you more detailed flags, but -L and -l will be useful enough.
Re-run tests without file change
For whatever reason you may need, typing q
in your terminal will force your tests to re-run
Next Week
I will be going over how to leverage selenium webdriver with Docker Compose and this setup. While it will be a short article with not a lot of code; it’s taken hours of trial and error with the various Docker images… kinda like this one. Personally, I’ve found the documentation for the official selenium images to be a bit obtuse… but i’m a dumby dumb dumb.