Fehler beim Unittesting mit pytest und flask

  • Python

Es gibt 1 Antwort in diesem Thema. Der letzte Beitrag () ist von siycah.

    Fehler beim Unittesting mit pytest und flask

    Moin,

    ich sitze vor einem Fehler beim Unittesting meiner Flask Application. Die Anwendung selber läuft problemlos aber beim Unittesting bekomme ich einen Fehler, den ich nicht nachvollziehen und auch nicht auflösen kann.
    Ich folge dabei dem Tutorial flask.palletsprojects.com/en/3.0.x/tutorial/

    Folgende Datenstruktur ist vorhanden:
    \inotodo
    \templates
    __init__.py
    db.py
    auth.py
    ....
    \tests
    _init__.py
    conftest.py
    test_auth.py
    ...

    conftest.py

    Python-Quellcode

    1. import os
    2. import tempfile
    3. import pytest
    4. from inotodo import create_app
    5. from inotodo.db import get_db, init_db
    6. with open(os.path.join(os.path.dirname(__file__), 'sql/sqldata.sql'), 'rb') as f:
    7. _data_sql = f.read().decode('utf8')
    8. @pytest.fixture
    9. def app():
    10. db_fd, db_path = tempfile.mkstemp()
    11. app = create_app({
    12. 'TESTING': True,
    13. 'DATABASE': db_path,
    14. })
    15. with app.app_context():
    16. init_db()
    17. get_db().executescript(_data_sql)
    18. yield app
    19. os.close(db_fd)
    20. os.unlink(db_path)
    21. @pytest.fixture
    22. def client(app):
    23. return app.test_client()
    24. @pytest.fixture
    25. def runner(app):
    26. return app.test_cli_runner()
    27. class AuthActions(object):
    28. def __init__(self, client):
    29. self._client = client
    30. def login(self, username='test', password='test'):
    31. return self._client.post(
    32. '/auth/login',
    33. data={'username': username, 'password': password}
    34. )
    35. def logout(self):
    36. return self._client.get('/auth/logout')
    37. @pytest.fixture
    38. def auth(client):
    39. return AuthActions(client)


    test_auth.py

    Quellcode

    1. import pytest
    2. from flask import g, session
    3. from inotodo.db import get_db
    4. def test_register(client, app):
    5. assert client.get('/auth/register').status_code == 200
    6. response = client.post(
    7. '/auth/register', data={'username': 'a', 'password': 'a'}
    8. )
    9. print(response.data)
    10. assert response.headers["Location"] == "/auth/login"
    11. with app.app_context():
    12. assert get_db().execute(
    13. "SELECT * FROM user WHERE username = 'a'",
    14. ).fetchone() is not None
    15. ...
    16. def test_login(client, auth):
    17. assert client.get('/auth/login').status_code == 200
    18. response = auth.login()
    19. assert response.headers["Location"] == "/"
    20. with client:
    21. client.get('/')
    22. assert session['user_id'] == 1
    23. assert g.user['username'] == 'test'


    Bei jedem Test ist der Inhalt von rsponse data nach dem aufruf von client.post

    HTML-Quellcode

    1. b'<!doctype html>\n<html lang=en>\n<title>400 Bad Request</title>\n<h1>Bad Request</h1>\n<p>The browser (or proxy) sent a request that this server could not understand.</p>\n'


    Und bei test_login diesen Fehler, der vielleicht auch Auskunft geben kann.

    Quellcode

    1. client = <FlaskClient <Flask 'inotodo'>>, auth = <tests.conftest.AuthActions object at 0x000001AEBD993FE0>
    2. def test_login(client, auth):
    3. assert client.get('/auth/login').status_code == 200
    4. response = auth.login()
    5. > assert response.headers["Location"] == "/"
    6. tests\test_auth.py:34:
    7. _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
    8. self = Headers([('Content-Type', 'text/html; charset=utf-8'), ('Content-Length', '167'), ('Vary', 'Cookie')]), key = 'Location'
    9. _get_mode = False
    10. def __getitem__(self, key, _get_mode=False):
    11. if not _get_mode:
    12. if isinstance(key, int):
    13. return self._list[key]
    14. elif isinstance(key, slice):
    15. return self.__class__(self._list[key])
    16. if not isinstance(key, str):
    17. raise BadRequestKeyError(key)
    18. ikey = key.lower()
    19. for k, v in self._list:
    20. if k.lower() == ikey:
    21. return v
    22. # micro optimization: if we are in get mode we will catch that
    23. # exception one stack level down so we can raise a standard
    24. # key error instead of our special one.
    25. if _get_mode:
    26. raise KeyError()
    27. > raise BadRequestKeyError(key)
    28. E werkzeug.exceptions.BadRequestKeyError: 400 Bad Request: The browser (or proxy) sent a request that this server could not understand.
    29. .venv\Lib\site-packages\werkzeug\datastructures\headers.py:72: BadRequestKeyError


    Diese Pytheon Pakete sind installiert:

    Quellcode

    1. Babel==2.14.0
    2. blinker==1.7.0
    3. click==8.1.7
    4. colorama==0.4.6
    5. coverage==7.4.4
    6. Flask==3.0.2
    7. flask-babel==4.0.0
    8. iniconfig==2.0.0
    9. itsdangerous==2.1.2
    10. Jinja2==3.1.3
    11. MarkupSafe==2.1.5
    12. packaging==24.0
    13. pluggy==1.4.0
    14. pytest==8.1.1
    15. pytz==2024.1
    16. setuptools==69.2.0
    17. Werkzeug==3.0.1


    Hat jemand eine Idee, was falsch läuft und wie man es beheben kann?

    Danke im Vorraus
    NB. Es ist doch schön, wenn man lesbare Namen vergibt. Siehe auch [VB.NET] Beispiele für guten und schlechten Code (Stil).

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „INOPIAE“ ()

    Du könntest mal mit Fiddler schauen, was genau die beiden Varianten an den Server schicken.
    Könnte etwas einfaches sein, wie der Content-Type, der unterschiedlich ist.

    Leider kenne ich mich mit Flask nicht aus (oder generell mit der Schlangensprache), weshalb ich nur bedingt Hinweise geben kann. Hoffentlich hilft dir das aber weiter!
    Quellcode lizensiert unter CC by SA 2.0 (Creative Commons Share-Alike)

    Meine Firma: Procyon Systems
    Meine Privatwebseite: SimonC.eu

    Bitte nicht wundern, wenn meine Aktivitäten im Forum etwas langsamer sind, ich baue gerade mein Nebengewerbe zum Vollgewerbe aus.
    Ich versuche auf euch zurückzukommen :)