【问题标题】:Spin up a local flask server for testing with pytest启动本地烧瓶服务器以使用 pytest 进行测试
【发布时间】:2018-11-16 11:15:38
【问题描述】:

我有以下问题。

我想在部署到生产环境之前在本地烧瓶服务器上运行测试。我为此使用pytest。我的 conftest.py 目前看起来像这样:

import pytest
from toolbox import Toolbox
import subprocess


def pytest_addoption(parser):
    """Add option to pass --testenv=local to pytest cli command"""
    parser.addoption(
        "--testenv", action="store", default="exodemo", help="my option: type1 or type2"
    )


@pytest.fixture(scope="module")
def testenv(request):
    return request.config.getoption("--testenv")


@pytest.fixture(scope="module")
def testurl(testenv):
        if testenv == 'local':
            return 'http://localhost:5000/'
        else:
            return 'https://api.domain.com/'

这允许我通过输入命令 pytest 来测试生产 api,并通过输入 pytest --testenv=local 来测试本地烧瓶服务器

此代码完美运行。

我的问题是每次我想像这样进行本地测试时,我都必须从终端手动实例化本地烧瓶服务器:

source ../pypyenv/bin/activate
python ../app.py

现在我想添加一个夹具,在测试开始时在后台启动一个终端,并在完成测试后关闭服务器。经过大量的研究和测试,我仍然无法让它工作。这是我添加到 conftest.py 中的行:

@pytest.fixture(scope="module", autouse=True)
def spinup(testenv):
    if testenv == 'local':
        cmd = ['../pypyenv/bin/python', '../app.py']
        p = subprocess.Popen(cmd, shell=True)
        yield
        p.terminate()
    else:
        pass

我得到的错误来自请求包,它说没有连接/被拒绝。

E requests.exceptions.ConnectionError: HTTPConnectionPool(host='localhost', port=5000): 超过最大重试次数 带有网址:/登录(由 NewConnectionError(': 建立新连接失败: [Errno 111] 连接被拒绝',))

/usr/lib/python3/dist-packages/requests/adapters.py:437: 连接错误

这对我来说意味着 app.py 下的烧瓶服务器不在线。有什么建议么?我对更优雅的选择持开放态度

【问题讨论】:

    标签: python-3.x flask subprocess pytest


    【解决方案1】:

    对于本地测试,Flask test_client 是一个更优雅的解决方案。请参阅Testing 上的文档。您可以为test_client 创建一个fixture 并使用它创建测试请求:

    @pytest.fixture
    def app():
        app = create_app()
        yield app
        # teardown here
    
    @pytest.fixture
    def client(app):
        return app.test_client()
    

    并像这样使用它:

    def test_can_login(client):
        response = client.post('/login', data={username='username', password='password'})
        assert response.status_code == 200
    

    如果唯一的问题是手动步骤,可以考虑使用 bash 脚本为您进行手动设置,然后执行 pytest

    【讨论】:

    • 谢谢!问题仍然是我无法使用它激活和运行 virtualenv。我已经阅读了烧瓶文档,但它没有显示如何激活 virtualenv。此外,问题的目标是在在线 api 上的测试与使用本地烧瓶服务器的测试之间切换。如果我在所有测试函数中都必须使用“客户端”作为参数,那么它们通常也不能用于针对在线 api 进行测试。
    • 我认为尝试从 python 应用程序中激活虚拟环境没有意义,但您可以编写一个 bash 脚本来手动执行您现在正在执行的任何操作。
    【解决方案2】:

    有了一个 bash 脚本(感谢@ekusela),我现在终于成功实现了我想要的。 我在新的终端窗口中添加了一个调用 bashscript spinserver.sh 的夹具。这在ubuntu中有效,不同环境下的命令不同(其他环境见Execute terminal command from python in new terminal window?)。

    @pytest.fixture(scope="session", autouse=True)
    def client(testenv):
        if testenv != 'local':
            pass
        else:
            p = subprocess.Popen(['gnome-terminal', '-x', './spinserver.sh'])
            time.sleep(3)
            yield
    

    这里是非常简单的 bashscript

    #!/bin/bash
    cd ..
    source pypyenv/bin/activate
    python app.py
    
    • sleep 命令是必要的,因为服务器需要一些时间 初始化。
    • 不要忘记使您的 bash 脚本可执行(chmod u+x spinserver.sh)
    • 我尝试在yield 之后进行拆解,但 p.kill 并没有 关闭窗口。这对我来说是可以接受的,因为没关系 如果我必须手动关闭终端窗口,我什至可以看到 必要时进行烧瓶调试

    【讨论】:

      【解决方案3】:

      我为此目的使用以下内容,以便测试配置也保留在测试服务器中

      @pytest.fixture(scope="session")
      def app():
          db_fd, db_path = tempfile.mkstemp()
      
          app = create_app({
              'TESTING': True,
              'DATABASE': db_path
          })
      
          yield app
      
          os.close(db_fd)
          os.unlink(db_path)
      
      from flask import request
      def shutdown_server():
          func = request.environ.get('werkzeug.server.shutdown')
          if func is None:
              raise RuntimeError('Not running with the Werkzeug Server')
          func()
      
      @pytest.fixture
      def server(app):
          @app.route('/shutdown',methods=('POST',))
          def shutdown():
              shutdown_server()
              return 'Shutting down server ...'
      
          import threading    
          t = threading.Thread(target=app.run)
          yield t.start()
      
          import requests
          requests.post('http://localhost:5000/shutdown')
      
      

      参考文献

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2019-01-04
        • 1970-01-01
        • 2017-08-15
        • 1970-01-01
        • 2016-11-29
        • 1970-01-01
        • 1970-01-01
        • 2014-03-12
        相关资源
        最近更新 更多