diff --git a/Pipfile b/Pipfile
new file mode 100644
index 0000000000000000000000000000000000000000..994ac947c2120ba55a724d421a4341a027a68362
--- /dev/null
+++ b/Pipfile
@@ -0,0 +1,20 @@
+[[source]]
+url = "https://pypi.org/simple"
+verify_ssl = true
+name = "pypi"
+
+[packages]
+attrs = "*"
+requests = "*"
+psutil = "*"
+
+[dev-packages]
+pytest = "*"
+pytest-cov = "*"
+wheel = "*"
+pyinstaller = "*"
+ipython = "*"
+pathlib2 = {version = "*", markers="python_version < '3.6'"}
+
+[requires]
+python_version = "3.5"
diff --git a/Pipfile.lock b/Pipfile.lock
new file mode 100644
index 0000000000000000000000000000000000000000..31e2c28ccca6c60a1b5ece07f2e7214b6ae850cd
--- /dev/null
+++ b/Pipfile.lock
@@ -0,0 +1,321 @@
+{
+    "_meta": {
+        "hash": {
+            "sha256": "ef7dc91825f95fb13543a1da77acb02ef4066d89922eed4d7e962b0a57032307"
+        },
+        "pipfile-spec": 6,
+        "requires": {
+            "python_version": "3.5"
+        },
+        "sources": [
+            {
+                "name": "pypi",
+                "url": "https://pypi.org/simple",
+                "verify_ssl": true
+            }
+        ]
+    },
+    "default": {
+        "attrs": {
+            "hashes": [
+                "sha256:10cbf6e27dbce8c30807caf056c8eb50917e0eaafe86347671b57254006c3e69",
+                "sha256:ca4be454458f9dec299268d472aaa5a11f67a4ff70093396e1ceae9c76cf4bbb"
+            ],
+            "index": "pypi",
+            "version": "==18.2.0"
+        },
+        "certifi": {
+            "hashes": [
+                "sha256:339dc09518b07e2fa7eda5450740925974815557727d6bd35d319c1524a04a4c",
+                "sha256:6d58c986d22b038c8c0df30d639f23a3e6d172a05c3583e766f4c0b785c0986a"
+            ],
+            "version": "==2018.10.15"
+        },
+        "chardet": {
+            "hashes": [
+                "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae",
+                "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691"
+            ],
+            "version": "==3.0.4"
+        },
+        "idna": {
+            "hashes": [
+                "sha256:156a6814fb5ac1fc6850fb002e0852d56c0c8d2531923a51032d1b70760e186e",
+                "sha256:684a38a6f903c1d71d6d5fac066b58d7768af4de2b832e426ec79c30daa94a16"
+            ],
+            "version": "==2.7"
+        },
+        "psutil": {
+            "hashes": [
+                "sha256:1c19957883e0b93d081d41687089ad630e370e26dc49fd9df6951d6c891c4736",
+                "sha256:1c71b9716790e202a00ab0931a6d1e25db1aa1198bcacaea2f5329f75d257fff",
+                "sha256:3b7a4daf4223dae171a67a89314ac5ca0738e94064a78d99cfd751c55d05f315",
+                "sha256:3e19be3441134445347af3767fa7770137d472a484070840eee6653b94ac5576",
+                "sha256:6e265c8f3da00b015d24b842bfeb111f856b13d24f2c57036582568dc650d6c3",
+                "sha256:809c9cef0402e3e48b5a1dddc390a8a6ff58b15362ea5714494073fa46c3d293",
+                "sha256:b4d1b735bf5b120813f4c89db8ac22d89162c558cbd7fdd298866125fe906219",
+                "sha256:bbffac64cfd01c6bcf90eb1bedc6c80501c4dae8aef4ad6d6dd49f8f05f6fc5a",
+                "sha256:bfcea4f189177b2d2ce4a34b03c4ac32c5b4c22e21f5b093d9d315e6e253cd81"
+            ],
+            "index": "pypi",
+            "version": "==5.4.8"
+        },
+        "requests": {
+            "hashes": [
+                "sha256:65b3a120e4329e33c9889db89c80976c5272f56ea92d3e74da8a463992e3ff54",
+                "sha256:ea881206e59f41dbd0bd445437d792e43906703fff75ca8ff43ccdb11f33f263"
+            ],
+            "index": "pypi",
+            "version": "==2.20.1"
+        },
+        "urllib3": {
+            "hashes": [
+                "sha256:61bf29cada3fc2fbefad4fdf059ea4bd1b4a86d2b6d15e1c7c0b582b9752fe39",
+                "sha256:de9529817c93f27c8ccbfead6985011db27bd0ddfcdb2d86f3f663385c6a9c22"
+            ],
+            "version": "==1.24.1"
+        }
+    },
+    "develop": {
+        "altgraph": {
+            "hashes": [
+                "sha256:d6814989f242b2b43025cba7161fc1b8fb487a62cd49c49245d6fd01c18ac997",
+                "sha256:ddf5320017147ba7b810198e0b6619bd7b5563aa034da388cea8546b877f9b0c"
+            ],
+            "version": "==0.16.1"
+        },
+        "atomicwrites": {
+            "hashes": [
+                "sha256:0312ad34fcad8fac3704d441f7b317e50af620823353ec657a53e981f92920c0",
+                "sha256:ec9ae8adaae229e4f8446952d204a3e4b5fdd2d099f9be3aaf556120135fb3ee"
+            ],
+            "version": "==1.2.1"
+        },
+        "attrs": {
+            "hashes": [
+                "sha256:10cbf6e27dbce8c30807caf056c8eb50917e0eaafe86347671b57254006c3e69",
+                "sha256:ca4be454458f9dec299268d472aaa5a11f67a4ff70093396e1ceae9c76cf4bbb"
+            ],
+            "index": "pypi",
+            "version": "==18.2.0"
+        },
+        "backcall": {
+            "hashes": [
+                "sha256:38ecd85be2c1e78f77fd91700c76e14667dc21e2713b63876c0eb901196e01e4",
+                "sha256:bbbf4b1e5cd2bdb08f915895b51081c041bac22394fdfcfdfbe9f14b77c08bf2"
+            ],
+            "version": "==0.1.0"
+        },
+        "coverage": {
+            "hashes": [
+                "sha256:09e47c529ff77bf042ecfe858fb55c3e3eb97aac2c87f0349ab5a7efd6b3939f",
+                "sha256:0a1f9b0eb3aa15c990c328535655847b3420231af299386cfe5efc98f9c250fe",
+                "sha256:0cc941b37b8c2ececfed341444a456912e740ecf515d560de58b9a76562d966d",
+                "sha256:10e8af18d1315de936d67775d3a814cc81d0747a1a0312d84e27ae5610e313b0",
+                "sha256:1b4276550b86caa60606bd3572b52769860a81a70754a54acc8ba789ce74d607",
+                "sha256:1e8a2627c48266c7b813975335cfdea58c706fe36f607c97d9392e61502dc79d",
+                "sha256:2b224052bfd801beb7478b03e8a66f3f25ea56ea488922e98903914ac9ac930b",
+                "sha256:447c450a093766744ab53bf1e7063ec82866f27bcb4f4c907da25ad293bba7e3",
+                "sha256:46101fc20c6f6568561cdd15a54018bb42980954b79aa46da8ae6f008066a30e",
+                "sha256:4710dc676bb4b779c4361b54eb308bc84d64a2fa3d78e5f7228921eccce5d815",
+                "sha256:510986f9a280cd05189b42eee2b69fecdf5bf9651d4cd315ea21d24a964a3c36",
+                "sha256:5535dda5739257effef56e49a1c51c71f1d37a6e5607bb25a5eee507c59580d1",
+                "sha256:5a7524042014642b39b1fcae85fb37556c200e64ec90824ae9ecf7b667ccfc14",
+                "sha256:5f55028169ef85e1fa8e4b8b1b91c0b3b0fa3297c4fb22990d46ff01d22c2d6c",
+                "sha256:6694d5573e7790a0e8d3d177d7a416ca5f5c150742ee703f3c18df76260de794",
+                "sha256:6831e1ac20ac52634da606b658b0b2712d26984999c9d93f0c6e59fe62ca741b",
+                "sha256:77f0d9fa5e10d03aa4528436e33423bfa3718b86c646615f04616294c935f840",
+                "sha256:828ad813c7cdc2e71dcf141912c685bfe4b548c0e6d9540db6418b807c345ddd",
+                "sha256:85a06c61598b14b015d4df233d249cd5abfa61084ef5b9f64a48e997fd829a82",
+                "sha256:8cb4febad0f0b26c6f62e1628f2053954ad2c555d67660f28dfb1b0496711952",
+                "sha256:a5c58664b23b248b16b96253880b2868fb34358911400a7ba39d7f6399935389",
+                "sha256:aaa0f296e503cda4bc07566f592cd7a28779d433f3a23c48082af425d6d5a78f",
+                "sha256:ab235d9fe64833f12d1334d29b558aacedfbca2356dfb9691f2d0d38a8a7bfb4",
+                "sha256:b3b0c8f660fae65eac74fbf003f3103769b90012ae7a460863010539bb7a80da",
+                "sha256:bab8e6d510d2ea0f1d14f12642e3f35cefa47a9b2e4c7cea1852b52bc9c49647",
+                "sha256:c45297bbdbc8bb79b02cf41417d63352b70bcb76f1bbb1ee7d47b3e89e42f95d",
+                "sha256:d19bca47c8a01b92640c614a9147b081a1974f69168ecd494687c827109e8f42",
+                "sha256:d64b4340a0c488a9e79b66ec9f9d77d02b99b772c8b8afd46c1294c1d39ca478",
+                "sha256:da969da069a82bbb5300b59161d8d7c8d423bc4ccd3b410a9b4d8932aeefc14b",
+                "sha256:ed02c7539705696ecb7dc9d476d861f3904a8d2b7e894bd418994920935d36bb",
+                "sha256:ee5b8abc35b549012e03a7b1e86c09491457dba6c94112a2482b18589cc2bdb9"
+            ],
+            "version": "==4.5.2"
+        },
+        "decorator": {
+            "hashes": [
+                "sha256:2c51dff8ef3c447388fe5e4453d24a2bf128d3a4c32af3fabef1f01c6851ab82",
+                "sha256:c39efa13fbdeb4506c476c9b3babf6a718da943dab7811c206005a4a956c080c"
+            ],
+            "version": "==4.3.0"
+        },
+        "future": {
+            "hashes": [
+                "sha256:67045236dcfd6816dc439556d009594abf643e5eb48992e36beac09c2ca659b8"
+            ],
+            "version": "==0.17.1"
+        },
+        "ipython": {
+            "hashes": [
+                "sha256:a5781d6934a3341a1f9acb4ea5acdc7ea0a0855e689dbe755d070ca51e995435",
+                "sha256:b10a7ddd03657c761fc503495bc36471c8158e3fc948573fb9fe82a7029d8efd"
+            ],
+            "index": "pypi",
+            "version": "==7.1.1"
+        },
+        "ipython-genutils": {
+            "hashes": [
+                "sha256:72dd37233799e619666c9f639a9da83c34013a73e8bbc79a7a6348d93c61fab8",
+                "sha256:eb2e116e75ecef9d4d228fdc66af54269afa26ab4463042e33785b887c628ba8"
+            ],
+            "version": "==0.2.0"
+        },
+        "jedi": {
+            "hashes": [
+                "sha256:0191c447165f798e6a730285f2eee783fff81b0d3df261945ecb80983b5c3ca7",
+                "sha256:b7493f73a2febe0dc33d51c99b474547f7f6c0b2c8fb2b21f453eef204c12148"
+            ],
+            "version": "==0.13.1"
+        },
+        "macholib": {
+            "hashes": [
+                "sha256:ac02d29898cf66f27510d8f39e9112ae00590adb4a48ec57b25028d6962b1ae1",
+                "sha256:c4180ffc6f909bf8db6cd81cff4b6f601d575568f4d5dee148c830e9851eb9db"
+            ],
+            "version": "==1.11"
+        },
+        "more-itertools": {
+            "hashes": [
+                "sha256:c187a73da93e7a8acc0001572aebc7e3c69daf7bf6881a2cea10650bd4420092",
+                "sha256:c476b5d3a34e12d40130bc2f935028b5f636df8f372dc2c1c01dc19681b2039e",
+                "sha256:fcbfeaea0be121980e15bc97b3817b5202ca73d0eae185b4550cbfce2a3ebb3d"
+            ],
+            "version": "==4.3.0"
+        },
+        "parso": {
+            "hashes": [
+                "sha256:35704a43a3c113cce4de228ddb39aab374b8004f4f2407d070b6a2ca784ce8a2",
+                "sha256:895c63e93b94ac1e1690f5fdd40b65f07c8171e3e53cbd7793b5b96c0e0a7f24"
+            ],
+            "version": "==0.3.1"
+        },
+        "pathlib2": {
+            "hashes": [
+                "sha256:8eb170f8d0d61825e09a95b38be068299ddeda82f35e96c3301a8a5e7604cb83",
+                "sha256:d1aa2a11ba7b8f7b21ab852b1fb5afb277e1bb99d5dfc663380b5015c0d80c5a"
+            ],
+            "index": "pypi",
+            "markers": "python_version < '3.6'",
+            "version": "==2.3.2"
+        },
+        "pefile": {
+            "hashes": [
+                "sha256:4c5b7e2de0c8cb6c504592167acf83115cbbde01fe4a507c16a1422850e86cd6"
+            ],
+            "version": "==2018.8.8"
+        },
+        "pexpect": {
+            "hashes": [
+                "sha256:2a8e88259839571d1251d278476f3eec5db26deb73a70be5ed5dc5435e418aba",
+                "sha256:3fbd41d4caf27fa4a377bfd16fef87271099463e6fa73e92a52f92dfee5d425b"
+            ],
+            "markers": "sys_platform != 'win32'",
+            "version": "==4.6.0"
+        },
+        "pickleshare": {
+            "hashes": [
+                "sha256:87683d47965c1da65cdacaf31c8441d12b8044cdec9aca500cd78fc2c683afca",
+                "sha256:9649af414d74d4df115d5d718f82acb59c9d418196b7b4290ed47a12ce62df56"
+            ],
+            "version": "==0.7.5"
+        },
+        "pluggy": {
+            "hashes": [
+                "sha256:447ba94990e8014ee25ec853339faf7b0fc8050cdc3289d4d71f7f410fb90095",
+                "sha256:bde19360a8ec4dfd8a20dcb811780a30998101f078fc7ded6162f0076f50508f"
+            ],
+            "version": "==0.8.0"
+        },
+        "prompt-toolkit": {
+            "hashes": [
+                "sha256:c1d6aff5252ab2ef391c2fe498ed8c088066f66bc64a8d5c095bbf795d9fec34",
+                "sha256:d4c47f79b635a0e70b84fdb97ebd9a274203706b1ee5ed44c10da62755cf3ec9",
+                "sha256:fd17048d8335c1e6d5ee403c3569953ba3eb8555d710bfc548faf0712666ea39"
+            ],
+            "version": "==2.0.7"
+        },
+        "ptyprocess": {
+            "hashes": [
+                "sha256:923f299cc5ad920c68f2bc0bc98b75b9f838b93b599941a6b63ddbc2476394c0",
+                "sha256:d7cc528d76e76342423ca640335bd3633420dc1366f258cb31d05e865ef5ca1f"
+            ],
+            "version": "==0.6.0"
+        },
+        "py": {
+            "hashes": [
+                "sha256:bf92637198836372b520efcba9e020c330123be8ce527e535d185ed4b6f45694",
+                "sha256:e76826342cefe3c3d5f7e8ee4316b80d1dd8a300781612ddbc765c17ba25a6c6"
+            ],
+            "version": "==1.7.0"
+        },
+        "pygments": {
+            "hashes": [
+                "sha256:78f3f434bcc5d6ee09020f92ba487f95ba50f1e3ef83ae96b9d5ffa1bab25c5d",
+                "sha256:dbae1046def0efb574852fab9e90209b23f556367b5a320c0bcb871c77c3e8cc"
+            ],
+            "version": "==2.2.0"
+        },
+        "pyinstaller": {
+            "hashes": [
+                "sha256:a5a6e04a66abfcf8761e89a2ebad937919c6be33a7b8963e1a961b55cb35986b"
+            ],
+            "index": "pypi",
+            "version": "==3.4"
+        },
+        "pytest": {
+            "hashes": [
+                "sha256:3f193df1cfe1d1609d4c583838bea3d532b18d6160fd3f55c9447fdca30848ec",
+                "sha256:e246cf173c01169b9617fc07264b7b1316e78d7a650055235d6d897bc80d9660"
+            ],
+            "index": "pypi",
+            "version": "==3.10.1"
+        },
+        "pytest-cov": {
+            "hashes": [
+                "sha256:513c425e931a0344944f84ea47f3956be0e416d95acbd897a44970c8d926d5d7",
+                "sha256:e360f048b7dae3f2f2a9a4d067b2dd6b6a015d384d1577c994a43f3f7cbad762"
+            ],
+            "index": "pypi",
+            "version": "==2.6.0"
+        },
+        "six": {
+            "hashes": [
+                "sha256:70e8a77beed4562e7f14fe23a786b54f6296e34344c23bc42f07b15018ff98e9",
+                "sha256:832dc0e10feb1aa2c68dcc57dbb658f1c7e65b9b61af69048abc87a2db00a0eb"
+            ],
+            "version": "==1.11.0"
+        },
+        "traitlets": {
+            "hashes": [
+                "sha256:9c4bd2d267b7153df9152698efb1050a5d84982d3384a37b2c1f7723ba3e7835",
+                "sha256:c6cb5e6f57c5a9bdaa40fa71ce7b4af30298fbab9ece9815b5d995ab6217c7d9"
+            ],
+            "version": "==4.3.2"
+        },
+        "wcwidth": {
+            "hashes": [
+                "sha256:3df37372226d6e63e1b1e1eda15c594bca98a22d33a23832a90998faa96bc65e",
+                "sha256:f4ebe71925af7b40a864553f761ed559b43544f8f71746c2d756c7fe788ade7c"
+            ],
+            "version": "==0.1.7"
+        },
+        "wheel": {
+            "hashes": [
+                "sha256:196c9842d79262bb66fcf59faa4bd0deb27da911dbc7c6cdca931080eb1f0783",
+                "sha256:c93e2d711f5f9841e17f53b0e6c0ff85593f3b416b6eec7a9452041a59a42688"
+            ],
+            "index": "pypi",
+            "version": "==0.32.2"
+        }
+    }
+}
diff --git a/README.md b/README.md
index 19cb3e1d8479ce26bd30de40c917a5e8a99f89f3..d1a7049d8061eecd748b1aa6ce8c53974e3eb34e 100644
--- a/README.md
+++ b/README.md
@@ -13,7 +13,7 @@ There are two ways to install Flamenco Worker:
 - If you have a distributable zip file (see [Packaging for distribution](#packaging-for-distribution))
   unzip it, `cd` into it, then run `./flamenco-worker` (or `flamenco-worker.exe` on Windows).
 
-- If you have a copy of the source files, run `pip3 install -e .` then run `flamenco-worker`. This
+- If you have a copy of the source files, run `pipenv install` then run `flamenco-worker`. This
   requires Python 3.5.2 or newer.
 
 
diff --git a/requirements-dev.txt b/requirements-dev.txt
deleted file mode 100644
index 4f85618ef25a7e780deedc045f6993217e2a01d0..0000000000000000000000000000000000000000
--- a/requirements-dev.txt
+++ /dev/null
@@ -1,4 +0,0 @@
--r requirements-test.txt
-ipython
-pyinstaller
-wheel
diff --git a/requirements-test.txt b/requirements-test.txt
deleted file mode 100644
index 98ed669fe6479ac2986a0e3d3ee8ba4acbeaf18b..0000000000000000000000000000000000000000
--- a/requirements-test.txt
+++ /dev/null
@@ -1,9 +0,0 @@
--r requirements.txt
-
-# Primary dependencies
-pytest==3.0.5
-pytest-cov==2.4.0
-
-# Secondary dependencies
-coverage==4.2
-py==1.4.32
diff --git a/requirements.txt b/requirements.txt
deleted file mode 100644
index 4d7703b24dcf69ce72c56e373ac69e3cb76ea7a3..0000000000000000000000000000000000000000
--- a/requirements.txt
+++ /dev/null
@@ -1,3 +0,0 @@
-attrs==18.2.0
-requests==2.20.1
-psutil==5.4.8
diff --git a/tests/__init__.py b/tests/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/tests/test_commands_blender_render.py b/tests/test_commands_blender_render.py
index a59e6990616ab378b0628d03ffa76c770cf7c735..10780de277253c84bc656bfba5e83e095ae4df17 100644
--- a/tests/test_commands_blender_render.py
+++ b/tests/test_commands_blender_render.py
@@ -3,7 +3,7 @@ import subprocess
 
 from unittest.mock import patch, call
 
-from test_runner import AbstractCommandTest
+from .test_runner import AbstractCommandTest
 
 
 class BlenderRenderTest(AbstractCommandTest):
@@ -81,7 +81,7 @@ class BlenderRenderTest(AbstractCommandTest):
 
     def test_cli_args(self):
         """Test that CLI arguments in the blender_cmd setting are handled properly."""
-        from mock_responses import CoroMock
+        from .mock_responses import CoroMock
 
         filepath = str(Path(__file__).parent)
         settings = {
@@ -114,7 +114,7 @@ class BlenderRenderTest(AbstractCommandTest):
             )
 
     def test_python_expr(self):
-        from mock_responses import CoroMock
+        from .mock_responses import CoroMock
 
         filepath = str(Path(__file__).parent)
         settings = {
diff --git a/tests/test_commands_copy_file.py b/tests/test_commands_copy_file.py
index 78408d30cacb0453440d54e364689ec2e2ccf7f8..1d87ca21dd629f44c575871514cceb71294ab614 100644
--- a/tests/test_commands_copy_file.py
+++ b/tests/test_commands_copy_file.py
@@ -1,7 +1,7 @@
 from pathlib import Path
 import os
 
-from test_runner import AbstractCommandTest
+from .test_runner import AbstractCommandTest
 
 
 class CopyFileTest(AbstractCommandTest):
diff --git a/tests/test_commands_merge_exr.py b/tests/test_commands_merge_exr.py
index 6f2d1371aae683f9ef41964db6b4317b6a296cd7..0cae920bd79b15b052781d538a9d476b75c7f353 100644
--- a/tests/test_commands_merge_exr.py
+++ b/tests/test_commands_merge_exr.py
@@ -1,6 +1,6 @@
 from pathlib import Path
 
-from test_runner import AbstractCommandTest
+from .test_runner import AbstractCommandTest
 
 
 class MergeProgressiveRendersCommandTest(AbstractCommandTest):
diff --git a/tests/test_commands_move_out_of_way.py b/tests/test_commands_move_out_of_way.py
index fce01100d1df7edf8e53ef25f97998ea2dab4cc0..c4693c376b2ce8526ce7efa0a7153f5d991d56a7 100644
--- a/tests/test_commands_move_out_of_way.py
+++ b/tests/test_commands_move_out_of_way.py
@@ -1,7 +1,7 @@
 from pathlib import Path
 import os
 
-from test_runner import AbstractCommandTest
+from .test_runner import AbstractCommandTest
 
 
 class MoveOutOfWayTest(AbstractCommandTest):
diff --git a/tests/test_commands_move_to_final.py b/tests/test_commands_move_to_final.py
index 8b9976b8a625d0b290698b7ca442fde58796fa50..e760ccbeedad0019a108380b3ffc8376006f55bf 100644
--- a/tests/test_commands_move_to_final.py
+++ b/tests/test_commands_move_to_final.py
@@ -1,7 +1,7 @@
 import os
 from pathlib import Path
 
-from test_runner import AbstractCommandTest
+from .test_runner import AbstractCommandTest
 
 
 class MoveToFinalTest(AbstractCommandTest):
diff --git a/tests/test_commands_remove_tree.py b/tests/test_commands_remove_tree.py
index 8308612f041a8c56f659b30a4050c3ab84c7a437..ecf3475a8d6463e369d46e4ea47b63e493907a23 100644
--- a/tests/test_commands_remove_tree.py
+++ b/tests/test_commands_remove_tree.py
@@ -1,7 +1,7 @@
 from pathlib import Path
 import os
 
-from test_runner import AbstractCommandTest
+from .test_runner import AbstractCommandTest
 
 
 class RemoveTreeTest(AbstractCommandTest):
diff --git a/tests/test_commands_subprocess.py b/tests/test_commands_subprocess.py
index cd60f395137fa15cae2bf67f5b70fb069d2220c0..2240a43e22a444dffea95e4c39d12205518c60fc 100644
--- a/tests/test_commands_subprocess.py
+++ b/tests/test_commands_subprocess.py
@@ -5,7 +5,7 @@ import tempfile
 
 import psutil
 
-from test_runner import AbstractCommandTest
+from .test_runner import AbstractCommandTest
 
 
 class PIDFileTest(AbstractCommandTest):
diff --git a/tests/test_coro_mock.py b/tests/test_coro_mock.py
index 2486600edabe8cc01796dbb8e35e0d9143b9e70f..cb9d6fe74cb32ec5daa19128bf6e40262e1f4fd5 100644
--- a/tests/test_coro_mock.py
+++ b/tests/test_coro_mock.py
@@ -10,7 +10,7 @@ class CoroMockTest(unittest.TestCase):
         self.loop = construct_asyncio_loop()
 
     def test_setting_return_value(self):
-        from mock_responses import CoroMock
+        from .mock_responses import CoroMock
 
         cm = CoroMock()
         cm.coro.return_value = '123'
diff --git a/tests/test_may_i_run.py b/tests/test_may_i_run.py
index 90e23b8bdde853676300076c302295d2a13decff..9da8a959b292f5a6fbf677d0525e76a326c1a42b 100644
--- a/tests/test_may_i_run.py
+++ b/tests/test_may_i_run.py
@@ -1,6 +1,6 @@
 from unittest.mock import Mock
 
-from abstract_worker_test import AbstractWorkerTest
+from .abstract_worker_test import AbstractWorkerTest
 
 
 class MayIRunTest(AbstractWorkerTest):
diff --git a/tests/test_pretask_check.py b/tests/test_pretask_check.py
index b97909a91101144d9353c91cae9c93338df75108..b77c2dc690a95adf31a7c853a3d1926345240588 100644
--- a/tests/test_pretask_check.py
+++ b/tests/test_pretask_check.py
@@ -3,7 +3,7 @@ import tempfile
 from pathlib import Path
 from unittest import mock
 
-from test_worker import AbstractFWorkerTest
+from .test_worker import AbstractFWorkerTest
 
 
 # Mock merge_with_home_config() so that it doesn't overwrite actual config.
@@ -34,7 +34,7 @@ class PretaskWriteCheckTest(AbstractFWorkerTest):
             self.worker.pretask_check_params.pre_task_check_write = (existing, )
 
     def test_happy_remove_file(self):
-        from mock_responses import EmptyResponse, CoroMock
+        from .mock_responses import EmptyResponse, CoroMock
 
         self.manager.post = CoroMock(return_value=EmptyResponse())
 
@@ -52,7 +52,7 @@ class PretaskWriteCheckTest(AbstractFWorkerTest):
         self.assertIsNone(self.worker.sleeping_task)
 
     def test_happy_not_remove_file(self):
-        from mock_responses import EmptyResponse, CoroMock
+        from .mock_responses import EmptyResponse, CoroMock
 
         self.manager.post = CoroMock(return_value=EmptyResponse())
 
@@ -74,7 +74,7 @@ class PretaskWriteCheckTest(AbstractFWorkerTest):
 
     @contextlib.contextmanager
     def write_check(self, post_run=None):
-        from mock_responses import EmptyResponse, CoroMock
+        from .mock_responses import EmptyResponse, CoroMock
 
         self.manager.post = CoroMock(return_value=EmptyResponse())
 
@@ -123,7 +123,7 @@ class PretaskReadCheckTest(AbstractFWorkerTest):
 
     @contextlib.contextmanager
     def read_check(self, post_run=None):
-        from mock_responses import EmptyResponse, CoroMock
+        from .mock_responses import EmptyResponse, CoroMock
 
         self.manager.post = CoroMock(return_value=EmptyResponse())
 
diff --git a/tests/test_runner.py b/tests/test_runner.py
index 3be294f69c2b05f734d3a48ab54a3d008fab45e6..60d15898eb627ead1d3137149c4244c84fd1ab1f 100644
--- a/tests/test_runner.py
+++ b/tests/test_runner.py
@@ -1,12 +1,12 @@
 import asyncio
 from unittest.mock import Mock, call
 
-from abstract_worker_test import AbstractWorkerTest
+from .abstract_worker_test import AbstractWorkerTest
 
 
 class AbstractCommandTest(AbstractWorkerTest):
     def setUp(self):
-        from mock_responses import CoroMock
+        from .mock_responses import CoroMock
         from flamenco_worker.worker import FlamencoWorker
         from flamenco_worker.runner import TaskRunner
         from flamenco_worker.cli import construct_asyncio_loop
diff --git a/tests/test_upstream_update_queue.py b/tests/test_upstream_update_queue.py
index 28575538ce75275f35578f160e8fde62b7f9e486..620ccc786cc7f99692d8fc0a2dd7e59a35759c5e 100644
--- a/tests/test_upstream_update_queue.py
+++ b/tests/test_upstream_update_queue.py
@@ -6,7 +6,7 @@ from unittest.mock import Mock
 
 import requests
 
-from abstract_worker_test import AbstractWorkerTest
+from .abstract_worker_test import AbstractWorkerTest
 
 
 class TaskUpdateQueueTest(AbstractWorkerTest):
@@ -14,7 +14,7 @@ class TaskUpdateQueueTest(AbstractWorkerTest):
         from flamenco_worker.upstream import FlamencoManager
         from flamenco_worker.upstream_update_queue import TaskUpdateQueue
         from flamenco_worker.cli import construct_asyncio_loop
-        from mock_responses import CoroMock
+        from .mock_responses import CoroMock
 
         self.asyncio_loop = construct_asyncio_loop()
         self.shutdown_future = self.asyncio_loop.create_future()
@@ -39,7 +39,7 @@ class TaskUpdateQueueTest(AbstractWorkerTest):
         Also tests connection errors and other HTTP error statuses.
         """
 
-        from mock_responses import JsonResponse, EmptyResponse
+        from .mock_responses import JsonResponse, EmptyResponse
 
         # Try different value types
         payload = {'key': 'value',
@@ -95,7 +95,7 @@ class TaskUpdateQueueTest(AbstractWorkerTest):
     def test_queue_persistence(self):
         """Check that updates are pushed, even when the process is stopped & restarted."""
 
-        from mock_responses import EmptyResponse
+        from .mock_responses import EmptyResponse
         from flamenco_worker.upstream_update_queue import TaskUpdateQueue
 
         # Try different value types
@@ -151,7 +151,7 @@ class TaskUpdateQueueTest(AbstractWorkerTest):
         """A 409 Conflict response should discard a queued task update.
         """
 
-        from mock_responses import TextResponse
+        from .mock_responses import TextResponse
 
         # Try different value types
         payload = {'key': 'value',
@@ -190,7 +190,7 @@ class TaskUpdateQueueTest(AbstractWorkerTest):
         while there are still updates in the local queue.
         """
 
-        from mock_responses import TextResponse
+        from .mock_responses import TextResponse
 
         # Try different value types
         payload = {'key': 'value',
diff --git a/tests/test_worker.py b/tests/test_worker.py
index 8ebf12a5ee078ac9086a5e9844fe716f49a1c6e8..8289badeb88d4912c7dc3babb1837658caa69d9b 100644
--- a/tests/test_worker.py
+++ b/tests/test_worker.py
@@ -6,7 +6,7 @@ from unittest.mock import Mock
 import asyncio
 import requests
 
-from abstract_worker_test import AbstractWorkerTest
+from .abstract_worker_test import AbstractWorkerTest
 
 
 class AbstractFWorkerTest(AbstractWorkerTest):
@@ -16,7 +16,7 @@ class AbstractFWorkerTest(AbstractWorkerTest):
         from flamenco_worker.worker import FlamencoWorker
         from flamenco_worker.runner import TaskRunner
         from flamenco_worker.upstream_update_queue import TaskUpdateQueue
-        from mock_responses import CoroMock
+        from .mock_responses import CoroMock
 
         self.asyncio_loop = construct_asyncio_loop()
         self.asyncio_loop.set_debug(True)
@@ -73,7 +73,7 @@ class WorkerStartupTest(AbstractFWorkerTest):
     @unittest.mock.patch('socket.gethostname')
     @unittest.mock.patch('flamenco_worker.config.merge_with_home_config')
     def test_startup_already_registered(self, mock_merge_with_home_config, mock_gethostname):
-        from mock_responses import EmptyResponse, CoroMock
+        from .mock_responses import EmptyResponse, CoroMock
 
         mock_gethostname.return_value = 'ws-unittest'
         self.manager.post = CoroMock(return_value=EmptyResponse())
@@ -94,7 +94,7 @@ class WorkerStartupTest(AbstractFWorkerTest):
     @unittest.mock.patch('flamenco_worker.config.merge_with_home_config')
     def test_startup_registration(self, mock_merge_with_home_config, mock_gethostname):
         from flamenco_worker.worker import detect_platform
-        from mock_responses import JsonResponse, CoroMock
+        from .mock_responses import JsonResponse, CoroMock
 
         self.worker.worker_id = None
         mock_gethostname.return_value = 'ws-unittest'
@@ -128,7 +128,7 @@ class WorkerStartupTest(AbstractFWorkerTest):
         """Test that startup is aborted when the worker can't register."""
 
         from flamenco_worker.worker import detect_platform, UnableToRegisterError
-        from mock_responses import JsonResponse, CoroMock
+        from .mock_responses import JsonResponse, CoroMock
 
         self.worker.worker_id = None
         mock_gethostname.return_value = 'ws-unittest'
@@ -167,7 +167,7 @@ class TestWorkerTaskExecution(AbstractFWorkerTest):
 
     def test_fetch_task_happy(self):
         from unittest.mock import call
-        from mock_responses import JsonResponse, CoroMock
+        from .mock_responses import JsonResponse, CoroMock
 
         self.manager.post = CoroMock()
         # response when fetching a task
@@ -223,7 +223,7 @@ class TestWorkerTaskExecution(AbstractFWorkerTest):
     def test_stop_current_task(self):
         """Test that stopped tasks get status 'canceled'."""
 
-        from mock_responses import JsonResponse, CoroMock
+        from .mock_responses import JsonResponse, CoroMock
 
         self.manager.post = CoroMock()
         # response when fetching a task
@@ -395,7 +395,7 @@ class WorkerShutdownTest(AbstractWorkerTest):
         from flamenco_worker.worker import FlamencoWorker
         from flamenco_worker.runner import TaskRunner
         from flamenco_worker.upstream_update_queue import TaskUpdateQueue
-        from mock_responses import CoroMock
+        from .mock_responses import CoroMock
 
         self.asyncio_loop = construct_asyncio_loop()
         self.asyncio_loop.set_debug(True)
@@ -439,7 +439,7 @@ class WorkerSleepingTest(AbstractFWorkerTest):
         self.worker.loop = self.loop
 
     def test_stop_current_task_go_sleep(self):
-        from mock_responses import JsonResponse, CoroMock
+        from .mock_responses import JsonResponse, CoroMock
 
         self.manager.post = CoroMock()
         # response when fetching a task