Skip to content

Commit 1b9eae3

Browse files
author
Fabien Coelho
committed
add pkg:name support for application
1 parent 12cb188 commit 1b9eae3

File tree

5 files changed

+46
-21
lines changed

5 files changed

+46
-21
lines changed

FlaskTester.py

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -420,7 +420,7 @@ def ft_authenticator():
420420
if "FLASK_TESTER_AUTH" in os.environ:
421421
auth.setPasses(os.environ["FLASK_TESTER_AUTH"].split(","))
422422

423-
yield auth
423+
yield auth
424424

425425

426426
@pytest.fixture
@@ -431,19 +431,31 @@ def ft_client(ft_authenticator):
431431
client: Client
432432

433433
if "FLASK_TESTER_URL" in os.environ: # pragma: no cover
434+
434435
app_url = os.environ["FLASK_TESTER_URL"]
435436
client = RequestClient(ft_authenticator, app_url, default_login)
437+
436438
elif "FLASK_TESTER_APP" in os.environ:
437-
pkg_name = os.environ["FLASK_TESTER_APP"]
439+
440+
# load app package
441+
pkg_name, app = os.environ["FLASK_TESTER_APP"], None
442+
app_names = ["app", "application", "create_app", "make_app"]
443+
if ":" in pkg_name: # override defaults
444+
pkg_name, app_name = pkg_name.split(":", 1)
445+
app_names = [app_name]
438446
pkg = importlib.import_module(pkg_name)
439-
if hasattr(pkg, "app"):
440-
app = getattr(pkg, "app")
441-
elif hasattr(pkg, "create_app"): # pragma: no cover
442-
app = getattr(pkg, "create_app")()
443-
else: # pragma: no cover
447+
# find app in package
448+
for name in app_names:
449+
if hasattr(pkg, name):
450+
app = getattr(pkg, name)
451+
if callable(app) and not hasattr(app, "test_client"):
452+
app = app()
453+
if not app: # pragma: no cover
444454
raise FlaskTesterError(f"cannot find Flask app in {pkg_name}")
445455
client = FlaskClient(ft_authenticator, app.test_client(), default_login)
456+
446457
else: # pragma: no cover
458+
447459
raise FlaskTesterError("no Flask application to test")
448460

449461
yield client

README.md

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ Only one set of tests is needed, switching from internal to external is
88
achieved through environment variables.
99

1010
![Status](https://github.com/zx80/flask-tester/actions/workflows/package.yml/badge.svg?branch=main&style=flat)
11-
![Tests](https://img.shields.io/badge/tests-9%20✓-success)
11+
![Tests](https://img.shields.io/badge/tests-10%20✓-success)
1212
![Coverage](https://img.shields.io/badge/coverage-100%25-success)
1313
![Issues](https://img.shields.io/github/issues/zx80/flask-tester?style=flat)
1414
![Python](https://img.shields.io/badge/python-3-informational)
@@ -48,18 +48,21 @@ def test_app(app):
4848
assert 'not in group "ADMIN"' in res.text
4949
```
5050

51-
This can be run against a server:
51+
This can be run against a (local) server:
5252

5353
```shell
54-
export FLASK_TESTER_URL="https://api.flask-tester.org"
55-
pytest test.py
54+
flask --app app:app run & # start flask app
55+
pid=$! # keep pid
56+
export FLASK_TESTER_URL="http://localhost:5000" # set app local url
57+
pytest test.py # run external tests
58+
kill $pid # stop app with pid
5659
```
5760

5861
Or locally with the Flask internal test infrastructure:
5962

6063
```shell
61-
export FLASK_TESTER_APP="app"
62-
pytest test.py
64+
export FLASK_TESTER_APP="app:app" # set app module
65+
pytest test.py # run internal tests
6366
```
6467

6568
See [`tests/app.py`](tests/app.py) for a sample
@@ -106,8 +109,9 @@ The package provides two fixtures:
106109
The application is expected to be already running when the test is started.
107110

108111
- `FLASK_TESTER_APP` package (filename with `.py`) to be imported for the application.
109-
- the application is expected to be named `app`
110-
- if not available, look and call for `create_app`
112+
- for `pkg:name`, `name` is the application in `pkg`.
113+
- for `pkg`, look for app as `app`, `application`, `create_app`, `make_app`.
114+
- in both cases, `name` is called if callable and not a Flask application.
111115

112116
Moreover:
113117
- `FLASK_TESTER_DEFAULT` default login for authentication, default is _None_.
@@ -187,6 +191,7 @@ please report any [issues](https://github.com/zx80/flask-tester/issues).
187191

188192
Improved documentation and tests.
189193
Raise an error when setting unusable passwords or tokens.
194+
Add support for `pkg:name` application syntax.
190195

191196
### 1.1 on 2024-03-13
192197

tests/Makefile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ PYTOPT =
1717
.PHONY: check.external
1818
check.external:
1919
source $(VENV)/bin/activate
20-
flask run --port=$(PORT) &
20+
flask --app app:app run --port=$(PORT) &
2121
flask_pid=$$!
2222
sleep $(SLEEP)
2323
export FLASK_TESTER_URL="http://localhost:$(PORT)"
@@ -27,7 +27,7 @@ check.external:
2727
.PHONY: check.internal
2828
check.internal:
2929
source $(VENV)/bin/activate
30-
export FLASK_TESTER_APP=app
30+
export FLASK_TESTER_APP="app:create_app"
3131
$(PYTEST) $(PYTOPT) test.py
3232

3333
.PHONY: check.coverage

tests/app.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,19 @@
1-
# Simple Flask application with authn and authz
1+
# Simple Test Flask application with authn and authz
22

33
import FlaskSimpleAuth as fsa
44

5+
TEST_PASSES: dict[str, str] = {"calvin": "clv-pass", "hobbes": "hbs-pass", "susie": "ss-pass", "moe": "m-pass"}
6+
57
app = fsa.Flask("app", FSA_MODE="dev", FSA_AUTH=["token", "param", "basic"])
68

7-
PASSES: dict[str, str] = {"calvin": "clv-pass", "hobbes": "hbs-pass", "susie": "ss-pass", "moe": "m-pass"}
9+
# authentication
10+
PASSDB = {login: app.hash_password(pwd) for login, pwd in TEST_PASSES.items()}
811

912
@app.get_user_pass
1013
def get_user_pass(login: str) -> str|None:
11-
return app.hash_password(PASSES[login]) if login in PASSES else None
14+
return PASSDB[login] if login in PASSDB else None
1215

16+
# authorization
1317
ADMINS: set[str] = {"calvin", "susie"}
1418

1519
@app.group_check("ADMIN")
@@ -32,3 +36,7 @@ def get_who_am_i(user: fsa.CurrentUser):
3236
@app.get("/admin", authorize="ADMIN")
3337
def get_admin(user: fsa.CurrentUser):
3438
return {"user": user, "isadmin": True}, 200
39+
40+
# for coverage
41+
def create_app():
42+
return app

tests/test.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
# set authn for ft_authenticator
1515
os.environ.update(
1616
FLASK_TESTER_ALLOW="bearer basic param",
17-
FLASK_TESTER_AUTH=",".join(f"{l}:{p}" for l, p in app.PASSES.items()),
17+
FLASK_TESTER_AUTH=",".join(f"{l}:{p}" for l, p in app.TEST_PASSES.items()),
1818
)
1919

2020
def test_sanity():

0 commit comments

Comments
 (0)