在pytest中的每次测试后如何完全拆解Flask应用程序?

时间:2019-08-01 15:41:04

标签: python flask pytest

我正在使用Pytest测试我的Flask应用。我有3个文件

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

如果我使用以下命令运行test_one.py test_two.py,它将正常运行。

python -m pytest tests/test_one.py

当我尝试使用以下命令运行所有测试时出现问题:

python -m pytest tests

我收到此错误:

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

我实际上并不为这个错误感到惊讶,因为init_app(app)被调用了两次(每个文件一次),并且我从未拆解实际的app

我的问题是,是否有一种方法可以完全拆解flask应用程序并为每个单独的测试重建它??我正在寻找进行拆解的实际命令(即{{1} })

编辑:如果可以解决此问题(无需创建新的解决方案),我愿意设置Flask应用测试环境的其他方法。

编辑2:我发现了this个问题。这很相似,可以解决我的问题,但是涉及到函数内部的导入。我怀疑必须有更好的方法。

conftest.py

app.teardown()

app / __ init __。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

4 个答案:

答案 0 :(得分:2)

我会在每个继承TestCase的文件中创建一个类。这样,您可以在每次测试之前使用函数setup()将您的testclient生成为实例变量。这样,您可以确保每个测试都具有相同的环境。

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)

答案 1 :(得分:0)

我正在考虑解决方案,但不涉及拆除flask应用程序。我不希望每个测试案例都拆掉它,因为这本身会导致每个测试案例的正常运行时间增加(除非flask应用程序初始化对于每个测试案例都是唯一的)

由于测试在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")

您可以将其包装在单例类中,而不是也采用上述方法

答案 2 :(得分:0)

以下各项的某些组合可能有效。我认为,这是完成此操作的旧方法(尽管我自己从未这样做过)。我的印象是pytest使用固定装置的概念来使直接teardown()变得不必要。

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

有关更多讨论,请参见this blog post on pytest

答案 3 :(得分:0)

最终,我的问题是我如何创建app。由于它是在__init__.py中作为全局变量创建的,因此我无法为每个测试对其进行 remake 。我重构了__init__.py(以及代码的其余部分),以使用create_app函数,在其中可以一次又一次地创建相同的应用程序。以下代码最终起作用。

app / __ 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

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