Tutorial zum Arbeiten mit Unit-Test

    • VB.NET

    Es gibt 4 Antworten in diesem Thema. Der letzte Beitrag () ist von INOPIAE.

      Tutorial zum Arbeiten mit Unit-Test

      Konzept von Unit-Testing

      Beim Arbeiten mit Unit-Tests ist die Idee, dass jede Funktion, die im Programm aufgerufen wird, durch entsprechende Tests auf ihre Funktionalität und das erwartete Verhalten geprüft wird.
      Diese Tests werden während der Entwicklung der Funktion geschrieben, bis die Funktion sich so verhält wie erwartet.
      Aber auch im weiteren Verlauf der Programmentwicklung wird diese Tests wiederholt, um zu gewährleisten das die Funktionalität immer noch gegeben ist.

      Konzept Test-Driven-Development
      Beim Test-Driven-Development ist der Ansatz, dass man eine Funktion in kleinen Schritten entwickelt, wobei jeder Veränderungsschritt getestet wird. Da hier aber ja nicht viele Codeänderung anstehen, sollten Fehler schnell gefunden werden.

      In der Praxis hat man dabei innerhalb einer Programm-Solution zwei Projekte: das eigentliche Programm-Projekt (Programm) und eine weiters Projekt für die Test (UnitTest)
      Im Idealfall geht man so vor:
      1. Es wird in Unittest ein Test T geschrieben, der versucht die Methode M im Programm aufzurufen. Im Programm wird nur der Methodenrumpf, ohne Übergabeparameter und irgendeiner Logik geschrieben. Nun wird der Test ausgeführt, um zu prüfen, dass der Aufruf überhaupt funktioniert.
      2. Nun wird der T so umgeschrieben, dass M mit einem Parameter aufgerufen wird. Entsprechend wird M so angepasst, dass der Parameter übergeben werden kann. Nun wird der Test ausgeführt, um zu prüfen, dass M den Parameter annimmt. Dabei wird die Rückgabe von M so einfach wie möglich gehalten.
      3. T wird weiter umgeschrieben, um zu prüfen, dass der Parameter auch richtig übergeben wurde. Dazu wird M entsprechend angepasst und wieder getestet.
      4. Nun wird ein erwartetes Ergebnis von M in den T vorgeben, und M so umgeschrieben, das das Ergebnis im Test verifiziert wird.
      5. Nun wird T um weitere Annahmen ergänzt, um Grenzfälle zu testen.
        Beispiel: Die Methode soll die Erfassung von Messwerten mit Datum speichern. Dann sollte beim Testen für die Datumseingabe nicht nur irgendein Datum getestet werden, sondern diese Fälle sollten berücksichtigt werden:
        - das aktuelle Datum- die Eingabe von Null/Nothing
        - ein Datum aus der Zukunft
        - ein Datum aus der näheren Vergangenheit
        ein Datum aus der weiteren Vergangenheit (ein Datum aus dem letzten Jahrtausend)
      Umsetzung in der Praxis mit Visual Studio

      Am Ende des Tutorials wird der Code in einem Beispielprojekt zur Verfügung gestellt.

      In Visual Studio wird ein neues Projekt angelegt. (Für das Beispiel eine Konsolen-App)
      Die Anwendung soll für eine Zahl alle zahlen von 1 bis zur Vorgabe berechnen: für 5 bedeutet dies: alle Werte von 1 bis 5 werden aufaddiert. 1 + 2 + 3 + 4 + 5 = 15

      Jetzt wird im Projektmappen-Explorer das Kontextmenü der Projektmappe mit der rechten Maustaste geöffnet und ein weiteres Projekt über Hinzufügen erstellt. Es wird eine Testvorlage genutzt. Für das Beispiel NUnit-Test.

      Es sind nun zwei Projekte im Projektmappen-Explorer zu sehen. Das Projekt der Konsolen-App (P) und das Testprojekt (T).

      T wird nun der Verweis auf P hinzugefügt.
      Dazu wird im Projektmappen-Explorer im Bereich T das Kontextmenu für die Abhängigkeiten geöffnet und "Projektverweis hinzufügen" genutzt.
      Dort wird unter Projekte – Projektmappe P vorgeschlagen. P wird ausgewählt und hinzugefügt. Damit sind die vorbereitenden Arbeiten abgeschlossen.
      Die von Visual Studio erstellte Testklasse sieht nun so aus:

      VB.NET-Quellcode

      1. Public Class TestReihe
      2. <SetUp>
      3. Public Sub Setup()
      4. End Sub
      5. <Test>
      6. Public Sub Test1()
      7. Assert.Pass()
      8. End Sub
      9. End Class


      Für NUnit gilt, dass einem Test immer die Zeile <Test> vorangestellt werden muss.
      Im Bereich Setup können globale Dinge vorgegeben werden, die immer bei einem Test ablaufen sollen. Mit Assert wird das Testergebnis überprüft.

      Jetz wird in P eine Klasse mit der Funktion Reihe erstellt:

      VB.NET-Quellcode

      1. Public Class ClsFunktion
      2. Public Function Reihe() As Short
      3. Return Nothing
      4. End Function
      5. End Class


      Die Funktion ist bewusst mit Short als Rückgabewert gesetzt worden, um den Umgang mit Fehlern aufzuzeigen. (Short kann nur ganzzahlige Werte von -32,768 bis 32,767 enthalten.)

      Und dazu wird ein Test geschrieben:

      VB.NET-Quellcode

      1. Public Class TestReihe
      2. Dim CF As New ClsFunktion
      3. <SetUp>
      4. Public Sub Setup()
      5. End Sub
      6. <Test>
      7. Public Sub TestReihe()
      8. Dim Ergebnis
      9. Ergebnis = CF.Reihe()
      10. Assert.AreEqual(0, Ergebnis)
      11. Assert.AreNotEqual(1, Ergebnis)
      12. End Sub
      13. End Class


      Mit Assert.AreEqual wird geprüft, dass der Wert von Ergebnis mit dem erwarteten Wert 0 übereinstimmt.
      Mit Assert.AreNotEqual wird geprüft, dass der Wert von Ergebnis mit dem erwarteten Wert 1 nicht übereinstimmt.
      Wenn eines der beiden Assert-Statements nicht erfüllt wird, wird beim Testen ein Fehler angezeigt.

      Jetzt wird der Test ausgeführt.
      Dazu wird der Test-Explorer geöffnet.
      Im Test Explorer wird nun das grüne Dreieck zum Ausführen der Test genutzt, um den Test auszuführen.
      Es sollte hier alles auf grün stehen.

      Als nächstes wird die Funktion um einen Parameter erweitert und geprüft, dass der Parameter auch in der Funktion ankommt:

      VB.NET-Quellcode

      1. Public Function Reihe(Eingabe As Short) As Short
      2. Return Eingabe
      3. End Function


      Der Test wird nun so umgeschrieben:

      VB.NET-Quellcode

      1. Public Sub TestReihe()
      2. Dim Eingabe As Short
      3. Dim Vorgabe As Short
      4. Dim Ergebnis
      5. Eingabe = 4
      6. Vorgabe = 4
      7. Ergebnis = CF.Reihe(Eingabe)
      8. Assert.AreEqual(Vorgabe, Ergebnis)
      9. Eingabe = 100
      10. Vorgabe = 100
      11. Ergebnis = CF.Reihe(Eingabe)
      12. Assert.AreEqual(Vorgabe, Ergebnis)
      13. End Sub


      Der Test wird wieder im Test-Explorer ausgeführt.
      Das Ergebnis sollte wieder grün sein.

      Nun wird die eigentliche Funktionalität geschrieben.

      VB.NET-Quellcode

      1. Public Function Reihe(Eingabe As Short) As Short
      2. Dim Ergebnis As Short
      3. For I As Short = 1 To Eingabe
      4. Ergebnis += I
      5. Next
      6. Return Ergebnis
      7. End Function


      Und der Test wird so angepasst:

      VB.NET-Quellcode

      1. Public Sub TestReihe()
      2. Dim Eingabe As Short
      3. Dim Vorgabe As Long
      4. Dim Ergebnis
      5. Eingabe = 4
      6. Vorgabe = 10
      7. Ergebnis = CF.Reihe(Eingabe)
      8. Assert.AreEqual(Vorgabe, Ergebnis)
      9. Eingabe = 1000
      10. Vorgabe = 500500
      11. Ergebnis = CF.Reihe(Eingabe)
      12. Assert.AreEqual(Vorgabe, Ergebnis)
      13. End Sub


      Beim Testen wird nun ein Fehler ausgegeben.
      Wenn im Test-Explorer der fehlerhafte (rote) Test ausgewählt wird, kann man diese Information erhalten:
      Nachricht: 
      System.OverflowException : Arithmetic operation resulted in an overflow.
      Stapelüberwachung: 
      ClsFunktion.Reihe(Int16 Eingabe) Zeile X
      TestReihe.TestReihe() Zeile Y

      Der Fehler resultiert daher, dass ab Eingabe von 256 das Ergebnis größer ist als 32767, welches der Maximalwert von Short ist.

      Nun muss man eine Entscheidung treffen, wie man weiter vorgehen möchte.
      Als erstes wird der Rückgabetyp der Funktion auf Long geändert.

      VB.NET-Quellcode

      1. Public FunctionReihe(Eingabe As Short) As Long
      2. Dim Ergebnis As Long
      3. For I As Short = 1 To Eingabe
      4. Ergebnis += I
      5. Next
      6. Return Ergebnis
      7. End Function


      Und der Test wiederholt. Nun sollte das Ergebnis wieder auf grün stehen.

      Nun wird in der Funktion die Eingabe von Short auf Long angepasst:

      VB.NET-Quellcode

      1. Public Function Reihe(Eingabe As Long) As Long
      2. Dim Ergebnis As Long
      3. For I As Long = 1 To Eingabe
      4. Ergebnis += I
      5. Next
      6. Return Ergebnis
      7. End Function


      Der Test wird so angepasst:

      VB.NET-Quellcode

      1. Public Sub TestReihe()
      2. Dim Eingabe As Long
      3. Dim Vorgabe As Long
      4. Dim Ergebnis
      5. Eingabe = 4
      6. Vorgabe = 10
      7. Ergebnis = CF.Reihe(Eingabe)
      8. Assert.AreEqual(Vorgabe, Ergebnis)
      9. Eingabe = 10000000000
      10. Vorgabe = 9223372034707292160
      11. Ergebnis = CF.Reihe(Eingabe)
      12. Assert.AreEqual(Vorgabe, Ergebnis)
      13. End Sub


      Der Test wird wieder ausgeführt. es gibt wieder einen Fehler:
      Nachricht: 
      System.OverflowException : Arithmetic operation resulted in an overflow.

      Stapelüberwachung: 
      ClsFunktion.Reihe(Int64 Eingabe) Zeile X
      TestReihe.TestReihe() Zeile Y

      Dieser Fehler kann jetzt nicht so ohne weiteres behoben werden.
      Damit man eine zielführende Fehlermeldung erhält, wird die Funktion so angepasst:

      VB.NET-Quellcode

      1. Public Function Reihe(Eingabe As Long) As Long
      2. Dim Ergebnis As Long
      3. If Eingabe >= 4294967296 Then
      4. Throw New OverflowException("Die Eingabe ist größer als 4294967296 und kann nicht verarbeitet werden.")
      5. End If
      6. For I As Long = 1 To Eingabe
      7. Ergebnis += I
      8. Next
      9. Return Ergebnis
      10. End Function


      Auf der Testseite wird ein zweiter Test für den Extremfall einfügt:

      VB.NET-Quellcode

      1. Public Sub TestReihe()
      2. Dim Eingabe As Long
      3. Dim Vorgabe As Long
      4. Dim Ergebnis
      5. Eingabe = 4
      6. Vorgabe = 10
      7. Ergebnis = CF.Reihe(Eingabe)
      8. Assert.AreEqual(Vorgabe, Ergebnis)
      9. Eingabe = 4294967295
      10. Vorgabe = 9223372034707292160
      11. Ergebnis = CF.Reihe(Eingabe)
      12. Assert.AreEqual(Vorgabe, Ergebnis)
      13. End Sub
      14. <Test>
      15. Public Sub TestReiheFehler()
      16. Dim Eingabe As Long
      17. 'Eingabe zu groß
      18. Eingabe = 4294967296
      19. Dim ex = Assert.Throws(Of OverflowException)(Function() CF.Reihe(Eingabe))
      20. Assert.AreEqual("Die Eingabe ist größer als 4294967296 und kann nicht verarbeitet werden.", ex.Message)
      21. End Sub


      Es wird wieder getestet.
      Es sollte wiederum alles grün sein.

      Nun werden im Test TestReihe noch Tests für Eingaben 0, negativen Werten und Nothing eingeführt.

      VB.NET-Quellcode

      1. Public Sub TestReihe()
      2. Dim Eingabe As Long
      3. Dim Vorgabe As Long
      4. Dim Ergebnis
      5. 'Einfache Eingabe
      6. Eingabe = 4
      7. Vorgabe = 10
      8. Ergebnis = CF.Reihe(Eingabe)
      9. Assert.AreEqual(Vorgabe, Ergebnis)
      10. 'Eingabe größter möglicher Wert
      11. Eingabe = 4294967295
      12. Vorgabe = 9223372034707292160
      13. Ergebnis = CF.Reihe(Eingabe)
      14. Assert.AreEqual(Vorgabe, Ergebnis)
      15. 'Eingabe 0
      16. Eingabe = 0
      17. Vorgabe = 0
      18. Ergebnis = CF.Reihe(Eingabe)
      19. Assert.AreEqual(Vorgabe, Ergebnis)
      20. 'Eingabe negativ
      21. Eingabe = -5
      22. Vorgabe = 0
      23. Ergebnis = CF.Reihe(Eingabe)
      24. Assert.AreEqual(Vorgabe, Ergebnis)
      25. 'Eingabe Nothing
      26. Eingabe = Nothing
      27. Vorgabe = 0
      28. Ergebnis = CF.Reihe(Eingabe)
      29. Assert.AreEqual(Vorgabe, Ergebnis)
      30. 'Eingabe großer negativer Wert
      31. Eingabe = -4294967296
      32. Vorgabe = 0
      33. Ergebnis = CF.Reihe(Eingabe)
      34. Assert.AreEqual(Vorgabe, Ergebnis)
      35. End Sub


      Die Tests werden wieder ausgeführt und sollten wieder alles in grün anzeigen.

      Mehr Sonderfälle fallen mir nicht ein.
      Nun kann die Funktion in die Anwendung P implementiert werden.
      Das Beispiel ist auf github.com/INOPIAE/inoUnitReihe hochgeladen. Die einzelnen Schritte können über die Commit Historie nachvollzogen werden.

      Testen einer Anwendung mit einer graphischen Oberfläche

      Soll eine Anwendung mit graphischer Oberfläche getestet werden, sollte die Lösung aus 3 Projekten bestehen:
      • Der Anwendung mit der Graphischen Oberfläche.
      • Mindestens einer Klassenbibliothek
      • Mindestens einem Testprojekt
      Die Anwendung sollte keine Businesslogik enthalten. Diese wird in die Klassenbiblothek(en) ausgelagert.

      Die Oberfläche muss mit anderen Testframeworks getestet werden.
      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 3 mal editiert, zuletzt von „INOPIAE“ ()

      Zwei Anmerkungen meinerseits:
      • Unit-Tests sind nur so gut wie der Implementierer. Fakultät ist nicht 1+2+3+…, sondern 1x2x3x…, durch Post#1-Korrektur hinfällig
      • m.E. sollte ein Test nicht geändert werden, sobald er die Funktionalität bestätigt hat, sondern so bleiben und die Testsuite sollte durch weitere Tests ergänzt werden. Nur so wird dem Leser klar, was alles getestet wird und warum, passende Benennung der Tests vorausgesetzt.
      Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

      Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht den Spekulatiusbackmodus wechseln.

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

      Habe mir das jetzt ein paar mal durchgelesen und weiß noch nicht ob ich alles verstanden habe.
      Dann das projekt heruntergeladen und ... siehe Bilder...
      Komme da wohl mit meinem alten Win7 und VS2013 nicht weit.

      Ich versuche das mal in einem Project umzusetzen, mache dazu dann ein Thema auf.
      Bilder
      • fakul1.jpg

        73,58 kB, 419×184, 77 mal angesehen
      • fakul2.jpg

        429,53 kB, 983×546, 80 mal angesehen
      Asperger Autistin. Brauche immer etwas um gewisse Sachen zu verstehen. :huh:
      Das Beispielprojekt wurde mit Visual Studio 2022 geschrieben, dies kann mit Visual Studio 2013 nicht laufen.

      Auf der angegebene Platform WIN7 / Visual Studion 2013 muss das Projekt leider neu erstellt werden.

      Ich werde auf Github einen entsprechenden Hinweis setzen.
      NB. Es ist doch schön, wenn man lesbare Namen vergibt. Siehe auch [VB.NET] Beispiele für guten und schlechten Code (Stil).