I am testing my Flask app using Pytest. I have 3 files
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.
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
.
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()
)
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).
Edit 2: I found this question. 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
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
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. This way you can use the function setup()
to generate your testclient as a Instance-Variable before each test. 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
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.
#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.
Ultimately my problem was how I created my app
. Since it was created as a gloval variable in __init__.py
, I could never remake it for each test. 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. The following code ended up working.
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
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.