Question How to test a blocking cli application in python?
What I am trying to accomplish
I have a manage.py
file, which is used to start the flask development web server (and other admin stuff that are out of the scope of this question). Typically I would run this as python manage.py runserver
, but for keeping this question simple and relevant I have edited that file (see Minimal, Reproducible Example) to just run the server. So the below command works
$ python manage.py
* Serving Flask app "app" (lazy loading)
* Environment: production...
The issue comes with testing the functionality of this cli application. Click has the CliRunner.invoke(), which allows one to test cli applications. But in this particular case, it fails as the server is running.
from manage import runserver
from click.testing import CliRunner
import requests
import unittest
class TestManage(unittest.TestCase):
def test_runserver(self):
runner = CliRunner()
runner.invoke(runserver)
# It blocks above, does not go below!
r = requests.get('http://127.0.0.1')
assert r.status_code == 200
if __name__ == '__main__':
unittest.main()
Attempts at solution
After trying many approaches I have found a "solution", which is to spawn a Process to start the server, run the tests and then terminating the process on exit (this is included in the example). This feels more like a hack rather than a "real" solution.
Minimal, Reproducible Example
-
Folder structure
flask_app/ - app.py - manage.py - test_manage.py
-
app.py
from flask import Flask app = Flask(__name__) @app.route('/') def hello(): return 'Hello, World!'
-
manage.py
import click from app import app @click.command() def runserver(): app.run() if __name__ == "__main__": runserver()
-
test_manage.py
from manage import runserver from multiprocessing import Process from click.testing import CliRunner import requests import unittest class TestManage(unittest.TestCase): def setUp(self): self.runner = CliRunner() self.server = Process( target=self.runner.invoke, args=(runserver, ) ) self.server.start() def test_runserver(self): r = requests.get('http://127.0.0.1') assert r.status_code == 200 def tearDown(self): self.server.terminate() if __name__ == '__main__': unittest.main()
And run the test using
$ python test_manage.py
.
Aucun commentaire:
Enregistrer un commentaire