Unterschiede

Hier werden die Unterschiede zwischen zwei Versionen angezeigt.

Link zu dieser Vergleichsansicht

Nächste Überarbeitung
Vorhergehende Überarbeitung
modul:m450:learningunits:lu16:aufgaben:behave [2025/10/23 08:18] – angelegt kmaurizimodul:m450:learningunits:lu16:aufgaben:behave [2025/10/23 09:11] (aktuell) – [Ausführung] kmaurizi
Zeile 1: Zeile 1:
-====== LU16.A01 – Behave Grundlagen ======+====== LU08.A01 – Behave Grundlagen======
  
 <WRAP center round todo 60%> <WRAP center round todo 60%>
 Erstelle mit **Behave** zwei kleine BDD-Übungen inkl. Feature-Dateien und Schrittdefinitionen. Erstelle mit **Behave** zwei kleine BDD-Übungen inkl. Feature-Dateien und Schrittdefinitionen.
-Arbeite sauber in der üblichen Projektstruktur (''features/'' und ''features/steps/''und nutze die Vorlagen unten.+**Wichtig:** Die Vorlagen enthalten **nur Gerüste und Hinweise**. Implementiere die Logik **selbst**. 
 +Arbeite in der üblichen Struktur (''features/'' und ''features/steps/'').
 </WRAP> </WRAP>
  
Zeile 10: Zeile 11:
 behave_project/ behave_project/
  ├── app/                 <-- (Produktivcode hier)  ├── app/                 <-- (Produktivcode hier)
- │    ├── converter.py + │    ├── converter.py    <-- TODO: selbst implementieren 
- │    └── auth.py+ │    └── auth.py         <-- TODO: selbst implementieren
  └── features/  └── features/
-      ├── temperature.feature +      ├── temperature.feature   <-- TODO 
-      ├── login.feature+      ├── login.feature         <-- TODO
       └── steps/       └── steps/
-           ├── temperature_steps.py +           ├── temperature_steps.py  <-- TODO 
-           └── login_steps.py+           └── login_steps.py        <-- TODO
 </code> </code>
 +
 +**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) ===== ===== Auftrag 1 – Temperatur-Umrechnung (Celsius ↔ Fahrenheit) =====
-**Ziel:** Schreibe BDD-Tests für eine einfache Umrechnungsfunktion.   +**Ziel:** BDD-Tests für eine Umrechnungsfunktion. Prüfe korrekte Werte **und** Fehlerverhalten (ungültige Eingaben).
-Teste korrekte Werte und Fehlerverhalten (z. B. ungültige Eingaben).+
  
-==== Feature-Datei ====+==== Feature-Datei (Gerüst + Hinweise) ====
 **Datei:** ''features/temperature.feature'' **Datei:** ''features/temperature.feature''
 <code> <code>
 +# 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 Feature: Temperaturumrechnung
   Damit ich Temperaturwerte standardisiert anzeigen kann   Damit ich Temperaturwerte standardisiert anzeigen kann
Zeile 44: Zeile 53:
  
   Scenario: Ungültige Eingabe (String)   Scenario: Ungültige Eingabe (String)
 +    # HINWEIS: Strings oder None sollen eine Exception auslösen
     Given eine ungültige Eingabe "heiss"     Given eine ungültige Eingabe "heiss"
     When ich eine Umrechnung ausführe     When ich eine Umrechnung ausführe
Zeile 49: Zeile 59:
 </code> </code>
  
-==== Schrittdefinitionen (Vorlage) ====+==== Schrittdefinitionen (Gerüst + Hinweise) ====
 **Datei:** ''features/steps/temperature_steps.py'' **Datei:** ''features/steps/temperature_steps.py''
 <code python> <code python>
 +# HINWEIS: Importiere deine Produktionsfunktionen aus app.converter
 +# from app.converter import c_to_f, f_to_c
 +
 from behave import given, when, then from behave import given, when, then
-from app.converter import c_to_f, f_to_c 
  
 @given("eine Celsius-Temperatur von {c:d}") @given("eine Celsius-Temperatur von {c:d}")
 def step_given_celsius(context, c): def step_given_celsius(context, c):
-    context.input_c = c+    # TODO: im context speichern (z. B. context.input_c = c
 +    pass
  
 @given("eine Fahrenheit-Temperatur von {f:d}") @given("eine Fahrenheit-Temperatur von {f:d}")
 def step_given_fahrenheit(context, f): def step_given_fahrenheit(context, f):
-    context.input_f = f+    # TODO: im context speichern 
 +    pass
  
 @given('eine ungültige Eingabe "{text}"') @given('eine ungültige Eingabe "{text}"')
 def step_given_invalid(context, text): def step_given_invalid(context, text):
-    context.invalid = text+    # TODO: im context speichern 
 +    pass
  
 @when("ich nach Fahrenheit umrechne") @when("ich nach Fahrenheit umrechne")
 def step_when_to_f(context): def step_when_to_f(context):
-    context.result = c_to_f(context.input_c)+    # TODO: c_to_f(context.input_c) aufrufen und Ergebnis speichern 
 +    # HINWEIS: beachte Datentypen (int/float) 
 +    pass
  
 @when("ich nach Celsius umrechne") @when("ich nach Celsius umrechne")
 def step_when_to_c(context): def step_when_to_c(context):
-    context.result = f_to_c(context.input_f)+    # TODO: f_to_c(context.input_f) aufrufen und Ergebnis speichern 
 +    pass
  
 @when("ich eine Umrechnung ausführe") @when("ich eine Umrechnung ausführe")
 def step_when_convert_any(context): def step_when_convert_any(context):
-    try: +    # TODOabsichtlich falschen Typ übergeben und Exception im context merken 
-        context.result = c_to_f(context.invalid) +    # Tipp: try/except und die Exception unter context.exc ablegen 
-    except Exception as exc: +    pass
-        context.exc = exc+
  
 @then("erhalte ich {expected:float} Fahrenheit") @then("erhalte ich {expected:float} Fahrenheit")
 def step_then_f(context, expected): def step_then_f(context, expected):
-    assert abs(context.result - expected) < 1e-6+    # TODO: numerischen Vergleich; Toleranz verwenden (z. B. 1e-6
 +    pass
  
 @then("erhalte ich {expected:float} Celsius") @then("erhalte ich {expected:float} Celsius")
 def step_then_c(context, expected): def step_then_c(context, expected):
-    assert abs(context.result - expected) < 1e-6+    # TODO: numerischen Vergleich 
 +    pass
  
 @then("wird ein ValueError ausgelöst") @then("wird ein ValueError ausgelöst")
 def step_then_error(context): def step_then_error(context):
-    assert isinstance(getattr(context, "exc", None), ValueError)+    # TODO: prüfen, ob context.exc existiert und vom Typ ValueError ist 
 +    pass
 </code> </code>
  
-==== Produktivcode (Vorlage) ====+==== Produktivcode (Gerüst + Hinweise) ====
 **Datei:** ''app/converter.py'' **Datei:** ''app/converter.py''
 <code python> <code python>
 +# 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): def c_to_f(celsius):
-    if not isinstance(celsius, (int, float)): +    # TODO: Eingabe validieren (isinstance) 
-        raise ValueError("celsius must be a number") +    # TODOUmrechnen und Rückgabe 
-    return celsius * 9.0 / 5.0 + 32.0+    raise NotImplementedError("c_to_f ist noch nicht implementiert")
  
 def f_to_c(fahrenheit): def f_to_c(fahrenheit):
-    if not isinstance(fahrenheit, (int, float)): +    # TODO: Eingabe validieren (isinstance) 
-        raise ValueError("fahrenheit must be a number") +    # TODOUmrechnen und Rückgabe 
-    return (fahrenheit - 32.0) * 5.0 / 9.0+    raise NotImplementedError("f_to_c ist noch nicht implementiert")
 </code> </code>
  
Zeile 112: Zeile 137:
  
 ===== Auftrag 2 – Login/Authentifizierung (Happy & Error Paths) ===== ===== Auftrag 2 – Login/Authentifizierung (Happy & Error Paths) =====
-**Ziel:** Schreibe BDD-Tests für einen sehr einfachen Login-Workflow.   +**Ziel:** BDD-Tests für einen sehr einfachen Login-Workflow. Prüfe: erfolgreicher Login, falsches Passwort, unbekannter Benutzer.
-Teste: erfolgreicher Login, falsches Passwort, unbekannter Benutzer.+
  
-==== Feature-Datei ====+==== Feature-Datei (Gerüst + Hinweise) ====
 **Datei:** ''features/login.feature'' **Datei:** ''features/login.feature''
 <code> <code>
 +# HINWEIS: Nutze Background, um einen Startzustand für alle Szenarien zu definieren.
 +
 Feature: Benutzer-Login Feature: Benutzer-Login
   Um den Zugang zu schützen   Um den Zugang zu schützen
Zeile 138: Zeile 164:
 </code> </code>
  
-==== Schrittdefinitionen (Vorlage) ====+==== Schrittdefinitionen (Gerüst + Hinweise) ====
 **Datei:** ''features/steps/login_steps.py'' **Datei:** ''features/steps/login_steps.py''
 <code python> <code python>
 +# HINWEIS: Importiere deinen AuthService aus app.auth
 +# from app.auth import AuthService
 +
 from behave import given, when, then from behave import given, when, then
-from app.auth import AuthService 
  
 @given('es existiert ein Benutzer "{username}" mit Passwort "{password}"') @given('es existiert ein Benutzer "{username}" mit Passwort "{password}"')
 def step_given_user_exists(context, username, password): def step_given_user_exists(context, username, password):
-    context.auth = AuthService() +    # TODO: AuthService im context instanzieren und Benutzer registrieren 
-    context.auth.register(username, password)+    # 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') @when('ich mich mit Benutzer "{username}" und Passwort "{password}" anmelde')
 def step_when_login(context, username, password): def step_when_login(context, username, password):
-    context.login_result = context.auth.login(username, password)+    # TODO: login() aufrufen und Ergebnis (True/False) im context speichern 
 +    pass
  
 @then("bin ich eingeloggt") @then("bin ich eingeloggt")
 def step_then_logged_in(context): def step_then_logged_in(context):
-    assert context.login_result is True+    # TODO: Ergebnis prüfen (True
 +    pass
  
 @then("ist der Login abgelehnt") @then("ist der Login abgelehnt")
 def step_then_denied(context): def step_then_denied(context):
-    assert context.login_result is False+    # TODO: Ergebnis prüfen (False
 +    pass
 </code> </code>
  
-==== Produktivcode (Vorlage) ====+==== Produktivcode (Gerüst + Hinweise) ====
 **Datei:** ''app/auth.py'' **Datei:** ''app/auth.py''
 <code python> <code python>
 +# 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: class AuthService:
     def __init__(self):     def __init__(self):
-        self._users = {}+        # TODO: In-Memory-"Datenbank" initialisieren 
 +        self._users = None  # TODO: durch {} ersetzen
  
     def register(self, username, password):     def register(self, username, password):
-        if not username or not password+        # TODOEingaben validieren (nicht leer), sonst ValueError 
-            raise ValueError("username and password required") +        # TODO: Benutzer speichern 
-        self._users[username] = password+        raise NotImplementedError("register ist noch nicht implementiert")
  
     def login(self, username, password):     def login(self, username, password):
-        if username not in self._users+        # TODOBenutzer nachschlagen und Passwort vergleichen 
-            return False +        # Rückgabe: True bei Erfolg, sonst False 
-        return self._users[username] == password+        raise NotImplementedError("login ist noch nicht implementiert")
 </code> </code>
  
Zeile 189: Zeile 227:
 </code> </code>
  
-**Erwartung:** Beide Features laufen grün.   +**Erwartung:** Beide Features laufen grün, nachdem ihr die Schrittdefinitionen **und** den Produktivcode implementiert habt.   
-Erweitere optional weitere Szenarien (z. B. Trimmen von Eingabenminimale PasswortlängeRandfälle bei Temperaturen).+ 
 +**Optional:** Ergänzt weitere Szenarien (z. B. Passwort-MinimallängeWhitespace-TrimmingGrenzwerte bei Temperaturen).
  
 ---- ----
  • modul/m450/learningunits/lu16/aufgaben/behave.1761200334.txt.gz
  • Zuletzt geändert: 2025/10/23 08:18
  • von kmaurizi