====== LU08.A01 – Behave Grundlagen======
Erstelle mit **Behave** zwei kleine BDD-Übungen inkl. Feature-Dateien und Schrittdefinitionen.
**Wichtig:** Die Vorlagen enthalten **nur Gerüste und Hinweise**. Implementiere die Logik **selbst**.
Arbeite in der üblichen Struktur (''features/'' und ''features/steps/'').
===== Projektstruktur (für beide Aufträge) =====
behave_project/
├── app/ <-- (Produktivcode hier)
│ ├── converter.py <-- TODO: selbst implementieren
│ └── auth.py <-- TODO: selbst implementieren
└── features/
├── temperature.feature <-- TODO
├── login.feature <-- TODO
└── steps/
├── temperature_steps.py <-- TODO
└── login_steps.py <-- TODO
**Hinweise allgemein:**
* ''behave'' erkennt Szenarien in ''.feature''-Dateien (Gherkin). Kommentare beginnen mit ''#''.
* Schrittdefinitionen befinden sich in ''features/steps/*.py'' und nutzen Dekoratoren ''@given/@when/@then''.
* Gemeinsame Daten im Szenario könnt ihr im ''context''-Objekt speichern (z. B. ''context.result'').
* Hardcodierte Zahlen in Szenarien sind ok für den Einstieg (z. B. 0°C → 32°F). Später könnt ihr ''Scenario Outline'' nutzen.
----
===== Auftrag 1 – Temperatur-Umrechnung (Celsius ↔ Fahrenheit) =====
**Ziel:** BDD-Tests für eine Umrechnungsfunktion. Prüfe korrekte Werte **und** Fehlerverhalten (ungültige Eingaben).
==== Feature-Datei (Gerüst + Hinweise) ====
**Datei:** ''features/temperature.feature''
# HINWEIS: Verwende klare, messbare Erwartungen (z. B. exakte Zahlen).
# Du kannst später Toleranzen im Code prüfen (z. B. |result-expected| < 1e-6).
Feature: Temperaturumrechnung
Damit ich Temperaturwerte standardisiert anzeigen kann
möchte ich zwischen Celsius und Fahrenheit korrekt umrechnen.
Scenario: Celsius zu Fahrenheit
Given eine Celsius-Temperatur von 0
When ich nach Fahrenheit umrechne
Then erhalte ich 32.0 Fahrenheit
Scenario: Fahrenheit zu Celsius
Given eine Fahrenheit-Temperatur von 212
When ich nach Celsius umrechne
Then erhalte ich 100.0 Celsius
Scenario: Ungültige Eingabe (String)
# HINWEIS: Strings oder None sollen eine Exception auslösen
Given eine ungültige Eingabe "heiss"
When ich eine Umrechnung ausführe
Then wird ein ValueError ausgelöst
==== Schrittdefinitionen (Gerüst + Hinweise) ====
**Datei:** ''features/steps/temperature_steps.py''
# HINWEIS: Importiere deine Produktionsfunktionen aus app.converter
# from app.converter import c_to_f, f_to_c
from behave import given, when, then
@given("eine Celsius-Temperatur von {c:d}")
def step_given_celsius(context, c):
# TODO: im context speichern (z. B. context.input_c = c)
pass
@given("eine Fahrenheit-Temperatur von {f:d}")
def step_given_fahrenheit(context, f):
# TODO: im context speichern
pass
@given('eine ungültige Eingabe "{text}"')
def step_given_invalid(context, text):
# TODO: im context speichern
pass
@when("ich nach Fahrenheit umrechne")
def step_when_to_f(context):
# TODO: c_to_f(context.input_c) aufrufen und Ergebnis speichern
# HINWEIS: beachte Datentypen (int/float)
pass
@when("ich nach Celsius umrechne")
def step_when_to_c(context):
# TODO: f_to_c(context.input_f) aufrufen und Ergebnis speichern
pass
@when("ich eine Umrechnung ausführe")
def step_when_convert_any(context):
# TODO: absichtlich falschen Typ übergeben und Exception im context merken
# Tipp: try/except und die Exception unter context.exc ablegen
pass
@then("erhalte ich {expected:float} Fahrenheit")
def step_then_f(context, expected):
# TODO: numerischen Vergleich; Toleranz verwenden (z. B. 1e-6)
pass
@then("erhalte ich {expected:float} Celsius")
def step_then_c(context, expected):
# TODO: numerischen Vergleich
pass
@then("wird ein ValueError ausgelöst")
def step_then_error(context):
# TODO: prüfen, ob context.exc existiert und vom Typ ValueError ist
pass
==== Produktivcode (Gerüst + Hinweise) ====
**Datei:** ''app/converter.py''
# HINWEIS: Implementiere die beiden Funktionen gemäß Formeln:
# F = C * 9/5 + 32
# C = (F - 32) * 5/9
# Validiere Eingaben: akzeptiere nur int/float, sonst ValueError.
def c_to_f(celsius):
# TODO: Eingabe validieren (isinstance)
# TODO: Umrechnen und Rückgabe
raise NotImplementedError("c_to_f ist noch nicht implementiert")
def f_to_c(fahrenheit):
# TODO: Eingabe validieren (isinstance)
# TODO: Umrechnen und Rückgabe
raise NotImplementedError("f_to_c ist noch nicht implementiert")
----
===== Auftrag 2 – Login/Authentifizierung (Happy & Error Paths) =====
**Ziel:** BDD-Tests für einen sehr einfachen Login-Workflow. Prüfe: erfolgreicher Login, falsches Passwort, unbekannter Benutzer.
==== Feature-Datei (Gerüst + Hinweise) ====
**Datei:** ''features/login.feature''
# HINWEIS: Nutze Background, um einen Startzustand für alle Szenarien zu definieren.
Feature: Benutzer-Login
Um den Zugang zu schützen
möchte ich, dass sich Benutzende mit Benutzername und Passwort anmelden.
Background:
Given es existiert ein Benutzer "alice" mit Passwort "secret"
Scenario: Erfolgreicher Login
When ich mich mit Benutzer "alice" und Passwort "secret" anmelde
Then bin ich eingeloggt
Scenario: Falsches Passwort
When ich mich mit Benutzer "alice" und Passwort "wrong" anmelde
Then ist der Login abgelehnt
Scenario: Unbekannter Benutzer
When ich mich mit Benutzer "bob" und Passwort "irrelevant" anmelde
Then ist der Login abgelehnt
==== Schrittdefinitionen (Gerüst + Hinweise) ====
**Datei:** ''features/steps/login_steps.py''
# HINWEIS: Importiere deinen AuthService aus app.auth
# from app.auth import AuthService
from behave import given, when, then
@given('es existiert ein Benutzer "{username}" mit Passwort "{password}"')
def step_given_user_exists(context, username, password):
# TODO: AuthService im context instanzieren und Benutzer registrieren
# HINWEIS: Leere Strings sollten in register() zu ValueError führen (testen könnt ihr später erweitern)
pass
@when('ich mich mit Benutzer "{username}" und Passwort "{password}" anmelde')
def step_when_login(context, username, password):
# TODO: login() aufrufen und Ergebnis (True/False) im context speichern
pass
@then("bin ich eingeloggt")
def step_then_logged_in(context):
# TODO: Ergebnis prüfen (True)
pass
@then("ist der Login abgelehnt")
def step_then_denied(context):
# TODO: Ergebnis prüfen (False)
pass
==== Produktivcode (Gerüst + Hinweise) ====
**Datei:** ''app/auth.py''
# HINWEIS: Minimaler AuthService ohne externe Abhängigkeiten.
# - _users als In-Memory-Dict
# - register(username, password): validiert Eingaben (nicht leer), speichert Benutzer
# - login(username, password): True bei passender Kombination, sonst False
class AuthService:
def __init__(self):
# TODO: In-Memory-"Datenbank" initialisieren
self._users = None # TODO: durch {} ersetzen
def register(self, username, password):
# TODO: Eingaben validieren (nicht leer), sonst ValueError
# TODO: Benutzer speichern
raise NotImplementedError("register ist noch nicht implementiert")
def login(self, username, password):
# TODO: Benutzer nachschlagen und Passwort vergleichen
# Rückgabe: True bei Erfolg, sonst False
raise NotImplementedError("login ist noch nicht implementiert")
----
===== Ausführung =====
Im Projekt-Hauptordner:
pip install behave
behave
**Erwartung:** Beide Features laufen grün, nachdem ihr die Schrittdefinitionen **und** den Produktivcode implementiert habt.
**Optional:** Ergänzt weitere Szenarien (z. B. Passwort-Minimallänge, Whitespace-Trimming, Grenzwerte bei Temperaturen).
----
{{tag>MXXX-LU08}}
[[https://creativecommons.org/licenses/by-nc-sa/4.0/|{{https://i.creativecommons.org/l/by-nc-sa/4.0/88x31.png}}]]