简体   繁体   English

如何在pytest中完成每次测试后完全拆解烧瓶应用程序?

[英]How to Completely Teardown Flask App After Each Test in pytest?

I am testing my Flask app using Pytest. 我正在使用Pytest测试我的Flask应用程序。 I have 3 files 我有3个文件

tests/
--conftest.py
--test_one.py
--test_two.py

If I run test_one.py or test_two.py using the following command, it will work without issue. 如果我使用以下命令运行test_one.py test_two.py ,它将正常工作。

python -m pytest tests/test_one.py

The issue arises when I try to run all tests with the following command: 当我尝试使用以下命令运行所有测试时出现问题:

python -m pytest tests

I get this error: 我收到此错误:

AssertionError: View function mapping is overwriting an existing endpoint function: serve_static

Im not actually surprised by this error since init_app(app) gets called twice (once per file) and I don't ever teardown the actual app . 我真的不会对这个错误感到惊讶,因为init_app(app)被调用两次(每个文件一次),我不会拆除实际的app

My question is, Is there a way to completely teardown a flask app and rebuild it for each individual test? 我的问题是, 有没有办法彻底拆除烧瓶应用程序并为每个单独的测试重建它? I am looking for the actual commands to do the tearing down (ie app.teardown() ) 我正在寻找拆除的实际命令(即app.teardown()

Edit: I am open to alternative ways of setting up a Flask app testing environment, if it would solve this problem (without creating new ones). 编辑:我愿意接受设置Flask应用测试环境的其他方法,如果能解决这个问题(不创建新问题)。

Edit 2: I found this question. 编辑2:我发现了这个问题。 It is similar and would solve my problem but it involves importing inside a function. 它是类似的,可以解决我的问题,但它涉及导入函数内部。 I suspect there has to be a better way. 我怀疑必须有更好的方法。

conftest.py conftest.py

import os
import pytest

from app import app, init_app
from config import TestConfig


@pytest.fixture(scope='function')
def client():

    app.config.from_object(TestConfig)

    with app.test_client() as client:
        with app.app_context():
            init_app(app)
        yield client

    try:
        os.remove('tests/testing.db')
    except FileNotFoundError:
        pass

app/__init__.py 应用程序/ __ init__.py

app = Flask(__name__)
app.url_map._rules.clear()
db = SQLAlchemy(app)
migrate = Migrate(app, db)

def init_app(app):
    ...

I would create a Class in each of your files which inherits TestCase. 我会在每个继承TestCase的文件中创建一个Class。 This way you can use the function setup() to generate your testclient as a Instance-Variable before each test. 这样,您可以使用函数setup()在每次测试之前将testclient生成为Instance-Variable。 This way you can be sure that each test has the same environment to work with. 这样,您可以确保每个测试都具有相同的环境。

from unittest import TestCase
from app import app as application

class TestApplication(TestCase):
  def setUp(self):
    self.client = application.app.test_client()

  def test_sth(self):
    r = self.client.get('/approute2sth')
    self.assertEqual(r.status_code, 200)

  def test_sth_else(self):
    r = self.client.get('/approute2sth_else')
    self.assertEqual(r.status_code, 200)

I am thinking of a solution but does not involve tearing down the flask app. 我正在考虑一个解决方案,但不涉及拆除烧瓶应用程序。 I do not prefer tearing it down for every test case, as that would itself lead to increased uptime for every test case ( unless the flask app initialization is unique to every test case ) 我不喜欢为每个测试用例拆掉它,因为这本身会导致每个测试用例的正常运行时间增加(除非烧瓶应用初始化对于每个测试用例都是唯一的)

Since tests run parallely in pytest, you can do the following 由于测试在pytest中并行运行,因此您可以执行以下操作

try:
    app
except NameError:
    app.config.from_object(TestConfig)
    with app.test_client() as client:
        with app.app_context():
            init_app(app)
else:
    print("App is already configured. Let us proceed with the test case")

You can wrap this around a singleton class instead of taking the above approach as well 您可以将它包装在单个类中,而不是采用上述方法

Some combination of the following might work. 以下某些组合可能有效。 This, I believe, was the old way to do it (though I never did it myself). 我相信这是做旧的方式(尽管我自己从未做过)。 I was under the impression that pytest uses the concept of fixtures to make direct teardown() unnecessary. 我的印象是pytest使用灯具的概念来直接teardown()

#clear current db session
db.session.remove()
#drop all tables in db
db.drop_all()
#remove app_context
app.app_context.pop()

See this blog post on pytest for more discussion. 有关更多讨论,请参阅pytest上的此博客文章

Ultimately my problem was how I created my app . 最终我的问题是我如何创建我的app Since it was created as a gloval variable in __init__.py , I could never remake it for each test. 由于它是在__init__.py创建的一个gloval变量,因此我无法为每次测试重新制作它。 I refactored __init__.py (and the rest of my code) to use a create_app function where I could create the same app over and over again. 我重构了__init__.py (以及我的其余代码)以使用create_app函数,我可以一遍又一遍地创建相同的应用程序。 The following code ended up working. 以下代码最终正常运行。

app/__init__.py 应用程序/ __ init__.py

def create_app(config)
    app = Flask(__name__)
    app.url_map._rules.clear()
    from .models import db
    app.config.from_object(config)
    with app.app_context():

    return app

def init_app(app):
    ...

conftest.py conftest.py

import os
import pytest

from app import create_app, init_app
from config import TestConfig


@pytest.fixture(scope='function')
def client():
    app = create_app()
    app.config.from_object(TestConfig)

    with app.test_client() as client:
        with app.app_context():
            from app.models import db
            init_app(app, db)
            yield client

    try:
        os.remove('tests/testing.db')
    except FileNotFoundError:
        pass

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM