Reputation: 6871
I don't know how to correctly use testing app version while unittesting (with pytest) flask cli command (with click) decorated with with_app_context
decorator. This decorator replaces pytest fixture app with the "normal", development application. I use app factory pattern.
My command in a simplified version looks like this (mind @with_appcontext
):
@click.command()
@click.option('--username', prompt='Username')
@click.option('--email', prompt='User E-Mail')
@click.option('--password', prompt='Password', confirmation_prompt=True, hide_input=True)
@with_appcontext # from flask.cli import with_appcontext
def createsuperuser(username, email, password):
user = User(
username=username,
email=email,
password=password,
active=True,
is_admin=True,
)
user.save()
Without @with_appcontext
unittests work just fine (they get the app injected by pytest), but the command itself does not, as it needs an app context.
My extracted pytest code:
# pytest fixtures
@pytest.yield_fixture(scope='function')
def app():
"""An application for the tests."""
_app = create_app(TestConfig)
ctx = _app.test_request_context()
ctx.push()
yield _app
ctx.pop()
@pytest.yield_fixture(scope='function')
def db(app):
"""A database for the tests."""
_db.app = app
with app.app_context():
_db.create_all()
yield _db
# Explicitly close DB connection
_db.session.close()
_db.drop_all()
@pytest.mark.usefixtures('db')
class TestCreateSuperUser:
# db fixture uses app fixture, works the same if app was injected here as well
def test_user_is_created(self, cli_runner, db):
result = cli_runner.invoke(
createsuperuser,
input='johnytheuser\[email protected]\nsecretpass\nsecretpass'
)
assert result.exit_code == 0
assert 'SUCCESS' in result.output
# etc.
All my tests using app
and db
fixtures work just fine apart from these decorated ones. I'm not sure how I should workaround this with_appcontext
decorator that sets the app itself.
Thank you in advance for any hint.
Upvotes: 4
Views: 2158
Reputation: 1
I had the same problem testing one of my flask commands. Although your approach works, I think it is valuable to have a look at the flask documentation here: https://flask.palletsprojects.com/en/1.1.x/testing/#testing-cli-commands
Flask has its own test runner for cli commands that probably has a fix to our problem builtin. So instead of patching the create_app
function with a lambda you could also just use app.test_cli_runner()
and it works out of the box.
Upvotes: 0
Reputation: 1390
The accepted answer helped me figure out a solution, but did not work out of the box. Here is what worked for me in case anyone else has a similar issue
@pytest.fixture(scope='session')
def script_info(app):
return ScriptInfo(create_app=lambda: app)
def test_user_is_created(self, cli_runner, db, script_info):
result = cli_runner.invoke(
createsuperuser,
input='johnytheuser\[email protected]\nsecretpass\nsecretpass',
obj=script_info,
)
Upvotes: 0
Reputation: 631
Inspiration taken from https://github.com/pallets/flask/blob/master/tests/test_cli.py#L254.
from flask.cli import ScriptInfo
@pytest.fixture
def script_info(app):
return ScriptInfo(create_app=lambda info: app)
In your test:
def test_user_is_created(self, cli_runner, db, script_info):
result = cli_runner.invoke(
createsuperuser,
input='johnytheuser\[email protected]\nsecretpass\nsecretpass',
obj=obj,
)
Upvotes: 2