# JUnit und NUnit und Daseinsberechtigung



## Gast2 (23. Nov 2009)

Moin,

ich versuche mich mal ernsthaft in JUnit (bzw. NUnit) einzuarbeiten und dabei entstehen mehr oder weniger Daseinberechtigungsfragen

das es super ist um Methoden zu testen die direkt ein Ergebnis liefern - keine Frage ... aber was mache ich mit einer Method (nenen wir sie Init()) nichts zurückliefert ... wie kann ich diese Methode testen *ohne* das ich die Datenkapselung aufweiche ... oder die Frage der Fragen - wie stelle ich sicher das meine Tests _richtig_ sind ... schreibe ich dafür wieder Test - das Endet dann zwangsläufig in einer Rekusion :autsch:

im Moment teste ich die Init-Methode so

```
@Test
	public void testInit() {
		try
		{
			Paket.Init();
			assertTrue(true);
		} catch(Exception ex)
		{
			fail(ex.getMessage());
		}
	}
```

wobei der fail nie ausgelöst wird, da innerhalb der Methode ich eine SMS bekomme und das komplette System (via exit(1)) abgewürgt wird

letztere Frage interessiert mich da ich demnächste mittels NUnit URL-Generatoren überprüfen will ... da ich die eigentlichen Kameras nicht zur Hand habe ... und bei ca. 100 URL's verliert man leicht den Überblick

hand, mogel


----------



## maki (24. Nov 2009)

Tests für ein existierendes Projekt zu schreiben ist mit Abstand der schwierigste Weg um Unittests zu erlernen, denn diese Projekte haben meist keine Struktur/Design/Architektur die testbar ist.
Wichtig ist hierbei, sich nicht entmutigen zu lassen und jemanden erfahrenen zu haben der einem helfen kann, denn solche Aufgaben sind nicht für Unittest Anfänger geeignet, ausser um sie zu frustrieren 



> aber was mache ich mit einer Method (nenen wir sie Init()) nichts zurückliefert ... wie kann ich diese Methode testen ohne das ich die Datenkapselung aufweiche ..


Mockobjekte/Fakes/Stubs, du musst irgendwo die Möglichkeit haben, Abhängigkeiten deiner Objekte zu setzen, implizite Abhängigkeiten stehen der Komponentenarchitektur entgegen und machen testen verdammt schwer.
Explizite Abhängigkeiten sind einfach zu testen, bringt uns zu DI 



> oder die Frage der Fragen - wie stelle ich sicher das meine Tests richtig sind ... schreibe ich dafür wieder Test


Das wären auf Dauer etwas viel Tests 



> wobei der fail nie ausgelöst wird, da innerhalb der Methode ich eine SMS bekomme und das komplette System (via exit(1)) abgewürgt wird


Dann stimmt einerseits der Test nicht, andererseits find ich das System.exit seltsam, aber ich weiss ja nicht was ihr da macht 
Jedenfalls musst du die Möglichkeit bekommen, den Teil des Systems zu ersetzen, der wirklich SMS verschickt und System.exit(0) aufruft, letzteres könnte man noch mit einem SecurityManager hinpfrimeln, für ersteres brauchst du einen Setter oder ein Konstruktorparamteter.

Nebenbei:

```
@Test
    public void testInit() {
        try
        {
            Paket.Init();
            assertTrue(true);
        } catch(Exception ex)
        {
            fail(ex.getMessage());
        }
    }
```
In Unittests macht meine kein explizites Exceptionhandling 
Wenn eine Exception fliegt, schlägt der Test fehl.

```
@Test
    public void testInit() throws Exception {
        Paket.Init();
        assertTrue(true);
    }
```
Das wäre der Test, wenn es darum geht das init() keine Exception wirft, das assertTrue könnte man sogar weglassen, je nach Konvention.

Interessanter wird es wenn du Mocks/Fakes zum Testen verwendest, sind dann sog. "White Box" tests, weil du den Mocks sagst welche Methoden vom zu testenden Subjekt aufgerufen werden und damit nicht nur die Schnittstellen sondern die internas getestet werden.

Wie gesagt, hast leider das Pech für eine "legacy" Anwendung Tests zu schreiben, normalerweise sind Test sehr sehr einfach und sollten nicht länger als 10-15% der Zeit für den Prod Code brauchen, idealerweise wird der Testcode geschrieben bevor der Prod Code geschrieben wurde, TDD eben, so hat der Prod Code auch immer eine testbares Design und man hat von Anfang an Tests.

Grad in der JavaLobby gefunden: An Introduction to Test-Driven Development with Legacy code | Javalobby


----------



## bygones (24. Nov 2009)

das meist hat ja maki schon geschrieben.

Dass seine Tests "richtig" sind sollte man nicht testen muessen, dass sie das richtige testen ist eine Erfahrung, die man lernt. Man neigt gern dazu eine Implementierung dahingegend zu testen, dass sie das was sie tut auch richtig tut, wohingegen man testen sollte, ob die Implementierung das tut, was sie tun *sollte*.

Dies geschieht zu 90% wenn man die Tests NACH dem eigentlich Code schreibt... schreibt man zuerst die Tests, bestimmen diese wie die Implementierung sein soll

zu deinem Bsp:

ein gern genommener Fehler ist sich zu denken "oh ich habe hier x codezeilen - nun muss ich fuer jede Zeile eine Test schreiben" - bestes bsp sind getter und setter - die zu testen ist sinnlos.

Weiterhin hast du eine statische Methodenaufruf, welcher bzgl Tests ganz schlecht ist... statische Methoden sollten so weit wie moeglich vermieden werden (ausser sie sind am ende des Callgraphs... Collections.sort / Math.abs etc).

Wenn eine Methode nix zurueckliefert kann man testen, dass die Collaborateure richtig bedient wurden (ueber Spies / Mocks etc).


Dein Bsp ist meiner Ansicht nach ein klassisches Bsp, was man nicht testen braucht... was soll der Test aussagen ? was willst du testen ? willst du Paket.Init(); testen ? dann tu das und nicht eine Methode die dieses aufruft.
Wenn du testen willst, dass die Methode Paket.Init() aufruft, dann kommst du mit dem statischen Konstrukt nicht weiter

Ein guter Ansatz sich ueber Testmoeglichkeiten / Testhindernisse etc zu informieren ist Mi?ko Hevery bzw seine "Clean Code Talks" auf youtube.com


----------



## Gast2 (3. Dez 2009)

bygones hat gesagt.:


> Man neigt gern dazu eine Implementierung dahingegend zu testen, dass sie das was sie tut auch richtig tut, wohingegen man testen sollte, ob die Implementierung das tut, was sie tun *sollte*.


das ist mir schon klar ... daher interessiert mich auch wie ich das sicher stellen kann das die Test das richtige testen


```
String [] befehle = Zaubern.RecreateCommand("ZAUBERE \"Hain der WasAuchImmer\" 2 \"GIB hell 1 Schwert\"".split(" "));
		assertTrue("ZAUBERE".equals(befehle[0]));
		assertTrue("Hein der WasAuchImmer".equals(befehle[1]));
		assertTrue("2".equals(befehle[2]));
		assertTrue("GIB hell 1 Schwert".equals(befehle[3]));
```

das Ding schlug fehl ... also entsprechend den Debugger anschmeißen ... das Beispiel ist jetzt klein ... aber was ist wenn ich den URL-Generator (siehe oben) entsprechend teste ... wenn da eine URL im Test falsch ist suche ich mir erstmal wieder einen Wolf im Quelltext bevor ich feststelle das der eigentliche Test fehlerhaft ist



> Dein Bsp ist meiner Ansicht nach ein klassisches Bsp, was man nicht testen braucht... was soll der Test aussagen ? was willst du testen ? willst du Paket.Init(); testen ? dann tu das und nicht eine Methode die dieses aufruft.


eigentlich wollte ich testen ob verschiedene Files von Platte nachgeladen wurden, wo weitere Informationen drinnen sind ... also kann ich an der Stelle nur testen ob ein (bzw. alle) Pakete nachgeladen wurden ... ob sie komplett nachgeladen wurden nicht


----------



## Gast2 (3. Dez 2009)

maki hat gesagt.:


> andererseits find ich das System.exit seltsam, aber ich weiss ja nicht was ihr da macht


das System abwürgen  ... in der SMS steht dann nur die Exception bzw. eine Fehlermeldung ... das Programm läuft außerdem nur 1x pro Woche ... damit weis ich zumindest das etwas schief gelaufen ist


----------

