Вопрос Как протестировать блокирующее приложение cli в Python?
Что я пытаюсь выполнить
У меня есть файл manage.py
, который используется для запуска веб-сервера разработки фляги (и других действий администратора, которые отсутствуюто объеме этого вопроса). Обычно я запускаю это как python manage.py runserver
, но для простоты и актуальности этого вопроса я отредактировал этот файл (см. «Минимальный, воспроизводимый пример»), чтобы просто запустить сервер. Таким образом, приведенная ниже команда работает
$ python manage.py
* Serving Flask app "app" (lazy loading)
* Environment: production...
Проблема заключается в тестировании функциональности этого приложения Cli. Click имеет CliRunner.invoke () , который позволяет тестировать cli приложения. Но в данном конкретном случае происходит сбой, так как сервер уже работает.
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()
Попытки решения
После попытки многих подходов я нашел «решение», который должен вызвать Process , чтобы запустить сервер, запустить тесты и затем завершить процесс при выходе (это включено в пример). Это больше похоже на взлом, а не на «реальное» решение.
Минимальный, воспроизводимый пример
Структура папки
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()
И запустите тест, используя $ python test_manage.py
.