Bestimmte Junit-Test aus eigenem Programm laufen lassen

Vielleicht ist es eine triviale oder dumme Frage und natürlich habe ich auch schon gegoogelt, vermutlich unter den falschen Suchbegriffen, denn ich habe zahlreiche Anleitungen zu JUnit gefunden, Seiten, die sich damit beschäftigen, wie man eine Oberfläche testet und so weiter und so fort. Vielleicht ist mein Vorgehen auch einfach ungewöhnlich, aber wie auch immer, so ist die momentane Lage:

Ich habe ein Programmpaket geschrieben, das zur Abarbeitung bestimmter Aufgaben ein Regelwerk[SUP]1[/SUP] verwendet, welches zu Teilen auf Textdateien außerhalb des Jars zugreift.

Um nun sicherzustellen, dass das Hinzufügen neuer Regeln oder generell Änderungen an den Textdateien nicht ehemals funktionierende Dinge kaputt macht, habe ich einige Vorkehrungen getroffen: Neben verschiedenen komplexeren Tests[SUP]2[/SUP] habe ich auch solche geschrieben, die sich der durch JUnit vorgegebenen Möglichkeiten bedienen[SUP]3[/SUP]. Anfänglich waren diese nur für mich selbst bestimmt und da reicht mir die von Eclipse mitgebrachte Unterstützung für JUnit mit dem (hoffentlich) grünen Balken und dem Durchzählen der abgearbeiteten Tests vollkommen aus.

Da nun andere Mitarbeiter das Regelwerk verändern können sollen, suche ich eine Möglichkeit, diese Tests auch innerhalb der zum Programm gehörenden Oberfläche[SUP]4[/SUP] auf Knopfdruck anzubieten.

Die Testklassen könnte ich auflisten, die darin befindlichen Methoden aufzulisten, wäre sehr unhandlich, da dies nach jedem neu eingefügten Test ergänzt werden müsste und ich sicher welche vergessen würde. Außerdem fehlt dann immer noch die eigentliche Durchführung der Tests und die grafische Ausgabe über Erfolg und Misserfolg.

Ich hab gelesen, dass JUnit3 noch eine Text- und Swing-Oberfläche mitgebracht haben soll. Allerdings nutze ich JUnit4 und will auch nicht deswegen wechseln.

Bevor ich jetzt also anfange irgendwas mit Reflections selbst zu bauen (Ermitteln der public-Methoden und Ausführen derselben), wollte ich fragen, ob ihr eine bessere, vielleicht sogar zu JUnit gehörige Methode dazu kennt oder vielleicht jemand schon einmal selbst einen ähnlichen Weg gegangen ist.

So ganz trivial dürfte es ja nicht werden, auch wenn ich solche Features wie zufällige Ausführungsreihenfolge und so weiter der einzelnen Tests gar nicht bräuchte.
Wobei… Vermutlich müsste man sich nur für jede Methode ein Objekt der Klasse erstellen, auf dem die Testmethode aufrufen und dann irgendwie herausbekommen, ob ein fail() aufgerufen wurde.

Dennoch muss ich ja nicht sinnlos das Rad neu erfinden:

Gibt es schon eine Möglichkeit, JUnit-Tests programmgesteuert selbst aufzurufen?

[HR][/HR]
Fußnoten:

  1. Das Regelwerk basiert auf regulären Ausdrücken.
  2. Diese Tests finden auf einer Datenbank statt.
  3. Diese liegen somit in meinem Programmpaket. Und davon gibt es inzwischen sehr sehr viele, die meist ganz kleine Dinge testen, dafür ist JUnit einfach unheimlich praktisch.
  4. Wohlgemerkt ist hier nicht Eclipse gemeint, sondern die selbst geschriebene GUI des Programms (oder genauer, eine der Oberflächen meines Programmpakets).

Suites und Testrunner helfen Dir hier weiter. Mit Suites kannst Du Tests gruppieren, mit Testrunner dann (auch aus Deinem Programm heraus) ausführen. Hier mal ein super kurzes Tutorial dazu: JUnit Suite Test

Super, das sieht sehr vielversprechend aus, danke!

Das klappt soweit. Das Problem das ich nun habe: Lass ich in Eclipse alle Tests laufen, führt er die in @Suite.SuiteClasses({ ... }) aufgeführten Tests zweimal aus. Vermutlich einmal ganz normal über ihre Klassen und außerdem noch einmal, weil sie in der Testsuite aufgeführt sind.

Kann man Eclipse irgendwie klarmachen, dass bei Run As > JUnit Tests Test-Suites ignoriert werden sollen?

Ich habe schon reichlich gegoogelt, aber nichts brauchbares gefunden.

Man kann die Test-Suite mit @Ignore auslassen, dann wird sie aber auch ignoriert, wenn sie wirklich ausgeführt werden soll.

Da die Tests eine gute Zeit dauern, ist die doppelte Ausführung tatsächlich ziemlich lästig. Hat da jemand eine gute Idee?

kannst du ein kleines Beispiel des Aufbaus geben?

und wenn, soweit eher kurz überflogen, die Tests programmgesteuert per Knopfdruck gestartet werden,
braucht es dann noch die ‚Run As > JUnit Tests‘ aus Eclipse?
gelegentlich für schnelle Test-Prüfung beim Programmieren ohne aufwendige Programmsteuerung?

wie steht es mit Aufteilung des Quellcodes in zwei bzw. mehr Klassen?
in einer die Einzeltests (nur dies ggfs. per Eclipse Run As), in einer anderen der höhere Code, der auch alle Tests ablaufen läßt?

edit: ‚Lass ich in Eclipse alle Tests laufen‘ geht wohl über alle Klassen?.. schade in diesem Fall


edit:
wenn es einen großen Test gibt, der viele startet, dann diesen einen vielleicht etwas schmutzig mit einem manuellen Schutz versehen:
eine Datei oder sonst etwas persistentes bei Durchführung speichern und vor jeder Durchführung schauen wie lange der letzte Test zurückliegt…,
ggfs. aktuelle Ausführung abbrechen

edit:
oder kann man nicht diesen großen Test, diese Testsuite, mit @Ignore ausstatten und wird dann standardmäßig ignoriert,
beim programmgesteuerten Button, direkten Aufruf, aber trotzdem ausgeführt? :wink:
etwas viel Spekulation nun in einem Posting versammelt

Ja, kann ich machen, auch wenn das im Link oben eigentlich schon dabei ist (siehe JUnit Suite Test).
Schiebe ich aber gern nochmal nach.

[QUOTE=SlaterB;136994]und wenn, soweit eher kurz überflogen, die Tests programmgesteuert per Knopfdruck gestartet werden,
braucht es dann noch die ‚Run As > JUnit Tests‘ aus Eclipse?[/quote]

Ja, denn in der Suite sind nur bestimmte Tests, die das Zusammenspiel meiner Klassen mit einem extern liegenden Regelwerk überprüfen. Diese soll ein Anwender in einer Oberfläche laufen lassen können (klappt).

Es gibt aber noch Haufenweise andere Tests. Diese testen meine Klassen und entsprechen der normalen Verwendung von JUnitTests. Diese will ich bei der Entwicklung natürlich auch laufen lassen, aber auch (einmal) die obigen Tests, da auch diese auf Programmierfehler hinweisen können.

Genau.

[QUOTE=SlaterB;136994]wie steht es mit Aufteilung des Quellcodes in zwei bzw. mehr Klassen?
in einer die Einzeltests (nur dies ggfs. per Eclipse Run As), in einer anderen der höhere Code, der auch alle Tests ablaufen läßt?[/quote]
Nein, ich möchte nicht beim Anlegen einer neuen Testklasse diese in irgendeine Sammlung einpflegen müssen.

Es kommt darauf an, auf was ich dabei rechtsklicke. Auf das ganze Projekt geht es über alle. Auf ein Paket (oder einen Source-Ordner) nur über die dort (und weiter unten) enthaltene Tests. Ich will aber nicht jedesmal „genau zielen“ müssen.

[QUOTE=SlaterB;136994]wenn es einen großen Test gibt, der viele startet, dann diesen einen vielleicht etwas schmutzig mit einem manuellen Schutz versehen:
eine Datei oder sonst etwas persistentes bei Durchführung speichern und vor jeder Durchführung schauen wie lange der letzte Test zurückliegt…,
ggfs. aktuelle Ausführung abbrechen[/QUOTE]

So wie dieser Tests aussieht, wüsste ich nicht wie das gehen sollte.

[QUOTE=SlaterB;136994]oder kann man nicht diesen großen Test, diese Testsuite, mit @Ignore ausstatten und wird dann standardmäßig ignoriert,
beim programmgesteuerten Button, direkten Aufruf, aber trotzdem ausgeführt? :wink:
etwas viel Spekulation nun in einem Posting versammelt[/QUOTE]
Dazu schrieb ich doch schon

Man kann die Test-Suite mit @Ignore auslassen, dann wird sie aber auch ignoriert, wenn sie wirklich ausgeführt werden soll.

*** Edit ***

Aufbau:

Es gibt zum einen viele Testklassen mit Testmethoden der Art


    @Test
    public void testeWas() {
        BlaBla blaBla = new BlaBla();
        // ...
    }

}```

Dann gibt es solche, mit Tests des Regelwerks, sehen im Prinzip genauso aus (vom Inhalt abgesehen):


```public class FooRulesTest {

    @Test
    public void fooOhneBarAberMitBaz() {
        // ...
    }

}```

Dann gibt es die Testsammlung:

```import org.junit.runner.RunWith;
import org.junit.runners.Suite;


@RunWith(Suite.class)
@Suite.SuiteClasses({
    FooRulesTest.class,
})

public class RulesTestSuite {

}

und eine Klasse, die diese Testsuite ausführt (noch ohne Gui, einfach zum Testen):


import org.junit.runner.JUnitCore;
import org.junit.runner.Result;
import org.junit.runner.notification.Failure;

public class RuleTestsRunner {

    public static void main(String[] args) {
        Result result = JUnitCore.runClasses(RulesTestSuite.class);

        System.out.println("result.wasSuccessful()   : " + result.wasSuccessful());
        System.out.println("result.getRunCount()     : " + result.getRunCount());
        System.out.println("result.getRunTime()      : " + result.getRunTime());
        System.out.println("result.getIgnoreCount()  : " + result.getIgnoreCount());
        System.out.println("result.getFailureCount() : " + result.getFailureCount());

        List<Failure> failures = result.getFailures();
        for (Failure failure : failures) {
           System.out.println(failure.toString());
        }

        System.out.println(result.wasSuccessful());
     }

}

Lasse ich alle Tests ausführen, wird ausgeführt:

  • testeWas()
  • fooOhneBarAberMitBaz()
  • fooOhneBarAberMitBaz()

Ich hoffe, das macht es klarer.

nur um diesen Gedanken weiterzuführen, es bleibt dirty:

java - TestSuite Setup in jUnit 4 - Stack Overflow
setup in TestSuite scheint möglich,
bei RuntimeException bei mir Abbruch, während andere Testklassen noch weiterlaufen,

also zumindest vorstellbar, in Setup ein letztes Datum zu prüfen und dort ggfs. abzubrechen

*** Edit ***

ach, auch hierzu zu schnell gedacht, die Suite kommt ja nur 1x dran…

ein möglicher Aufbau wäre wie folgt,
erfordert als Minimum an Aufwand in jeder Testklasse eine individuelle Methode a la

    public static void setUpClass()   {
        check(eigene Klasse);
    }

die check-Methode prüft ob diese Klasse schon einmal verwendet, innerhalb des Programms statisch abzulegen,
wenn ja, dann nicht nochmal Test ausführen,

so jede Testklasse nur einmal, das Kann auch Kombinationen geben wie
Einzelklasse A, B, C erst einzeln dran, dann Suite und A, B, C abgelehnt, aber D, E innerhalb der Suite ausgeführt,
dann wieder D, E einzeln dran und abgelehnt,

die JUnit-Anzeige kommt dazu mit den grünen Symbolen bei mir nicht mit, aber Failure-Zähler steht bei 0, das könnte reichen

Konstruktor statt setUp wäre bei gemeinsamer Basisklasse unsichtbarer zum Check ob schon ausgeführt,
aber geht nicht wenn mehrere Tests in einer Klasse, für jeden neuer Konstruktor-Aufruf

Beispiel

public class TestBase {
static Set clMap = new HashSet<>();

protected static void check(Class c) {
    System.out.println("check: " + c);
    if (!clMap.add(c))    {
        System.out.println("abgelehnt");
        throw new RuntimeException();
    }
}

}

public class TestA extends TestBase {
@BeforeClass
public static void setUpClass() {
check(TestA.class);
}

@Test
public void testA1()  {
    assertEquals(2, 2);
    System.out.println("testA1");
}

@Test
public void testA2()  {
    assertEquals(2, 2);
    System.out.println("testA2");
}

}

public class TestB extends TestBase {
@BeforeClass
public static void setUpClass() {
check(TestB.class);
}

@Test
public void testB1()    {
    assertEquals(2, 2);
    System.out.println("testB1");
}

}

@RunWith(Suite.class)
@Suite.SuiteClasses({
TestA.class,
TestB.class,
})
public class TestRun {
@BeforeClass
public static void setUpClass()
{
System.out.println(„TestRun setup“); // nur zur Reihenfolge der Ausgabe
}
}

so, all das wie gesagt nur um den Gedanken auszubauen zu etwas Ausführbaren,
keine allzu starke Empfehlung zu so drastischen Eingriff :wink:

Danke, das sieht wirklich noch recht dirty aus. Ich warte mal, ob jemand anderes hier noch eine schlaue Idee hat.

Irgendwie wirkt es so, als fehle mir nur das richtige Pragma @IgnoreSuiteWhileTestAll. :wink:

*** Edit ***

Ich hab noch dies hier gefunden, vielleicht auch eine Möglichkeit, in der Richtung zu denken:

A JUnit Rule to Conditionally Ignore Tests

Falls sich für

@ConditinalIgnore( condition = NotRunningFromSuite.class)

die Klasse NotRunningFromSuite sinnvoll bilden lässt.

eine weitere Idee, dirty, aber zumindest überschaubarer:
die Suite bricht standardmäßig ab (wiederum in setup), wenn nicht eine statische Variable gesetzt ist,
was nur in der programmgesteuerten Variante kurz vor Aufruf passiert :wink:


        Result result = junit.run(TestA.class, TestB.class);

tut es vielleicht auch ganz ohne Suite?

ob die Klassen dort oder in Annotation aufzulisten sind nimmt sich sicher nicht viel,
für mich ohne Annotation eh besser, auf jeden Fall eine Klasse weniger,

Testklassen-Liste auch dynamisch zusammenzustellen,
evtl. ohne manuelle Angabe automatisch zu suchen (wobei damit die JUnit- bzw. Eclipse-Mechanismen halb nachzubauen),

es ginge sicher auch Schleife und Einzeltest mit genaueren Ausgaben, welche Tests scheitern,
was aber im Programm nicht ganz so wichtig ist, das könnte dann auch in Eclipse geschehen wenn selten Fehler vorliegen

Super, das hilft. Danke!