Nicht noch ein JUnit-Thread

Hallo Ihr,
ich umreisse mal grob das Problem:
Stellt euch vor ihr habt Schüler die Java lernen und Aufgaben (Quellcodes) lösen, die sie dann hochladen. Die landen dann alle in einem Ordner, der wahrscheinlich nach der SchülerID benannt ist. Ich möchte nun wissen, ob deren Quellcodes in etwa das tun was sie sollen. Darum habe ich hier eine Testingklasse die sich alle eingelieferten Quellcodes vornimmt.
Problem: die Javaklassen liegen ja vorher noch nicht vor, wenn ich die Testingklasse schreibe - das heißt ich könnte jetzt in jede einzelne java Datei reingucken und den Namen der Klasse rausfischen. Dieser wird ja benötigt, wenn man für JUnit die zu testende Klasse initiiert. Das beste wird sein, wenn ich für jede Aufgabe eine Codehülle vorgebe. Die Methoden werden dann von den Schülern gefüllt. So habe ich die Kontrolle über deren Quellcode und kann diesen auch generalisiert testen. Aber das Problem ist wie gesagt, wie ich dem Testprogramm zur Laufzeit alle Klassen bekannt machen kann, die es zu testen hat. So lange ich auch überlege, ich komme nur auf zwei Ansätze.

  1. ich habe ein anderes Programm, dass den Ordner parst und den notwendigen Quellcode dann in die jeweilige Klasse schreibt. Das ist aber keine schöne Lösung wie ich finde
  2. ich arbeite mit EJB, wo die Klassen ja vorher auch nicht alle bekannt sind - aber davon möchte ich wirklich noch absehen. Ich möchte das nicht zu sehr aufblähen, sondern das lokal laufen lassen. Ohne Webserver.

Es wäre echt toll, wenn ihr mir eure Erfahrungen und Ideen posten könntet. Ich kann doch unmöglich der Erste sein, der auf so eine Idee kommt.

mit freundlichen Grüßen

T.

das sollte relativ einfach sein, du musst halt paar Vorgaben machen

  1. Hauptklasse mit den bestimmten Methoden müssen fest definiert sein
  2. Alle Klassen müssen an einem bestimmten Platz liegen

du musst dann halt beim Starten deines Test den Classpath auf den Ordner und da die Hauptklasse ja fest definiert ist kannst du einen fest gehackten Test durchlaufen lassen.

am einfachsten: die schueler sollen schon den Unit test schreiben. Das hilft ihnen auch gleich zum probieren ob das was sie geloest haben richtig ist (und ich hoffe mal sie schreiben so und so code um das auszuprobieren).
Dann kannst du JUnit ueber alle Testklassen laufen lassen.

Wenn du keine Ahnung hast was da wie wo drinsteht schauts nicht gut aus ohne sich nicht komplett den Hals zubrechen bei den Bemuehungen.
Eine feste Interface Vorgabe muss schonmal sein - aber auch dann ist das “ich hol mir mal alle Klassen in Ordner X, hoffe dass sie die Implementierungen sind die ich brauch und nicht Hilfsklassen und lade alle dynamisch und starte” nicht trivial - machbar, aber zu viel Aufwand imo

Du gibst doch die Aufgabe vor oder ? D.h. du hast es auch in der Hand a) die Funktionalitaet abzugrenzen und b) Schnittstellen zu definieren. Daher wuerde ich den Schuelern eine Testklasse mit der zu fordernden Funktionalitaet mitgeben, die mithochgeladen werden muss. Darauf kannst du dann JUnit laufen lassen

ya, ich denke das klingt noch am vernünftigsten. Danke für die Impulse. Und ya, ich gebe die Aufgabe vor, also kann ich auch die Vorgaben festsetzen.

So, mittlerweile habe ich Reflections entdeckt. Damit kann man genau das machen, was ich brauche. Das Problem ist allerdings, dass ich scheinbar irgendetwas falsch mache. Das Programm wirft mir immer eine “ClassNotFound”-Exception vor die Füße. Ausprobiert habe ich dabei schon den Quellcode aus “Java ist auch eine Insel” sowie diesen hier:

package ReflTest;

class CA 
{
	public void Hello()
	{
		System.out.println("Hallo Knollo");
	}
}

public class ReflTest {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		try {
			Class c = Class.forName("CA");
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}

}

Kann mir irgendwer verraten, warum er die Klassen nicht zur Laufzeit finden kann? Würde mich brennend interessieren.
Vielen Dank für eure Zeit.

Ok, ich habs zwar bereits vermutet, aber es ist erforderlich, dass man auch den Namen des packages mit angibt. Also in diesem Fall
Class c = Class.forName("ReflTest.CA");

Das Tutorial mit dem ich arbeitete verzichtete sicherlich auf Pakete, weshalb man das dort weglassen konnte.

befindet sich die Klasse CA im Claspath?
außerdem heißt deine Klasse nicht CA sondern ReflTest.CA
(Packages werden klein geschrieben :aetsch:)

ya, danke ich sah es eben (s. Edit) du warst schneller :stuck_out_tongue:

Es ist zum Ausrasten. Warum ist es so verdammt eklig mit mehreren Paketen zu arbeiten? Bzw. nicht das Arbeiten ist bescheuert, sondern das ausführen von Class-Dateien. Klar, jetzt könnte man sagen “Was für ein n00b”. Und was das angeht habe ich auch noch nicht viele Erfahrungen, aber es kann doch nicht wahr sein, dass man selbst mit Suchfunktion und Google nicht schlauer wird. Beim Ausführen des Programms wird mir eine NoClassDefFoundError-Excp geschmissen. Die wurde hier im Forum schon öfter geschmissen und überall heißt es einfach nur “einfach Classpath” anpassen. Aber das ist nicht so, wie ich mir das vorstelle.

man stelle sich vor ich befinde mich in Ordner src. Dort gibt es zwei Unterordner -> aufgaben und logik. Das es sich um zwei Pakete handelt, dürfte klar sein. In Logik befindet sich die Main.java wo dann schließlich via Reflection eine Klasse gesucht wird die in aufgaben liegt. Das er richtig kompiliert steht außer Frage, denn ich benutze nun
Class c = Class.forName("aufgaben.test"). Doch wird das Programm gestartet, kann er den aufgaben Ordner nicht finden. Gut, gehst du halt mal auf Konsolenebene und probierst dort herum. Das habe ich getan, ich habe -cp beansprucht.
Einmal aus dem Sourceordner heraus:
[Python]java logik/Main[/Python]
–> NoClassDefFound von aufgaben/test --> wrong name oder sowas

dann mit modifiziertem ClassPath:
[Python]java -cp .:aufgaben/ logik/Main[/Python]
Aber auch hier das Gleiche… wie funktioniert denn das nun? Ich dachte wenn ich ihm den Pfad zu der Klasse mitgebe, findet er alle .class-Dateien die dort enthalten sind. Aber reflection scheint über andere Pfade darauf zugreifen zu wollen, anders verstehe ich nicht, wieso er die Klassen nicht finden kann…

mit Packages ist es ganz einfach wenn man sie verstanden hat, nur bis es erst einmal Klick gemacht hat dauert es etwas

so wie ich das jetzt verstanden hab hast du so eine Struktur


src
 |- Main.java
 |-aufgaben - test.java
 \- logik - test.java

wenn du den ganzen Rammel compilierst sieht die Struktur ja genauso aus nur das sie im bin oder src Ordner liegt, wichtig ist du musst sie compilen :wink:

ich geh jetzt mal davon aus du hast sie einfach in src gebaut


src
 |- Main.class
 |-aufgaben - test.class
 \- logik - test.class

wenn du in src bist, dann startest du sie einfach mit

 java Main 

bist du auf der gleichen Ebene wie src, dann einfach mit

 java -cp src Main

hast du aber die Struktur


Main.class
Abgabe
 |-aufgaben - test.class
 \- logik - test.class

dann startest du von der gleichen Ebene wie die Main mit


java -cp .:Abgabe Main

Danke für die Antwort, ich muss mal sehen wie sie auf mein Problem passt, da der Verzeichnisbaum folgendermaßen aussieht:

src
|_ logik
       |_ Main.java
|_ aufgaben
       |_aufgabe.java

ich hatte probeweise auch das aufgaben-Paket mit in logik drin, aber das ging auch nicht

naja du musst sie jetzt noch compilen, weil die Java Dateien kannst du per Reflection nicht auslesen/ausführen
und wenn du sie compiliert hast dann ist es der letzte Fall von mir

die Dateien sind kompiliert und ich habe verstanden was deine Ausführungen mir erklärten. Das ist in etwa auch das Verhalten was ich davon erwartet habe. Aber er findet die Klassen trotzdem nicht.


public class test
{
    public static void main(String[] args)
    {
        System.out.println("Hallo Knolloe");
        try {
            Class c = Class.forName("testpack.aufgaben.Vectors");
        } catch (ClassNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}```

Fehlermeldung:

```pwd && java -cp .:testpack/aufgaben/ testpack/test
/home/asd/Code/java/Test/
Exception in thread "main" java.lang.NoClassDefFoundError: testpack/aufgaben/Vectors (wrong name: aufgaben/Vectors)
        at java.lang.ClassLoader.defineClass1(Native Method)
        at java.lang.ClassLoader.defineClassCond(ClassLoader.java:632)
        at java.lang.ClassLoader.defineClass(ClassLoader.java:616)
        at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:141)
        at java.net.URLClassLoader.defineClass(URLClassLoader.java:283)
        at java.net.URLClassLoader.access$000(URLClassLoader.java:58)
        at java.net.URLClassLoader$1.run(URLClassLoader.java:197)
        at java.security.AccessController.doPrivileged(Native Method)
        at java.net.URLClassLoader.findClass(URLClassLoader.java:190)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:307)
        at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:301)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:248)
        at java.lang.Class.forName0(Native Method)
        at java.lang.Class.forName(Class.java:169)
        at testpack.test.main(test.java:9)

wie sieht der Inhalt deiner Datei Vectors.java aus?


public class Vectors
{
    public void hello()
    {
        System.out.println("was geht ab Knollo");
    }
}```

Fehler gefunden, danke Eagle.. natürlich muss ich hier noch das package anpassen.. da fehlt das testpack davor. Jetzt muss ich nur noch Netbeans beibringen das auch so zu machen >_>

:wink:

so schnell noch ein paar Bugfixes eintragen und dann gehts nach Hause

argh!.. irgendwo habe ich aufgeschnappt, dass in der forName()-Methode die Paketnamen klein geschrieben werden müssen. Das ist aber nicht richtig. Wenn das Paket einen großen Anfangsbuchstaben hat, muss der auch groß in der Methode stehen T_T Man sollte eben doch nicht alles glauben oder wenn dann auch richtig lesen >_>
Es gehört aber zur guten Form Paketnamen klein zu schreiben.

[QUOTE=EagleEye]
(Packages werden klein geschrieben :aetsch:)[/QUOTE]

Das könnte diese Aussage gewesen sein :wink:
Das war aber auf den Packagenamen allgemein bezogen

hehe, dann warst du Schlawiner das. Meine Idee ist nun, das gesamte package aus dem Projekt zu lösen. Nun stellt sich aber das Problem, dass ich nicht weiß, wie ich der Applikation beibringen soll externe packages zu finden. Also ich verwende nun einen Ordner “Aufgaben” in dem dann die class-Dateien drin sind. Mein reflectionprogramm soll dann darauf zugreifen können, ohne das aufgaben-package im Projekt enthalten zu haben. Das Ziel wird sein, dass ich meinem Programm am Anfang sage, wo er die class-Dateien findet und diese dann via Reflection ansteuern kann. Geht das so ohne weiteres?

Achya, ich will davon absehen irgendwelche jars im Projekt einzubinden. Das Programm soll am Ende völlig autark funktionieren, ohne dem Wissen welche Aufgaben es gibt. Das soll man dann über die gui einstellen (es wird darauf hinauslaufen ne xml zu laden in der dann steht wo die Aufgaben sind und welche Methoden wie getestet werden)

ich versteh noch nicht so ganz warum du das Pferd soo von hinten aufzuegeln willst (ging das sprichwort so ?)

Du gibst deinen Schuelern die Aufgabe

a. schreibt eine Klasse namens X, die wenn sie Y bekommt Z zurueckgeben muss
b. schreibt einen JUnit Test der das Verhalten testet

das alles hochladen… und dann alle Testklassen ausfuehren lassen ?!

du kannst ja auch den JUnit test selbst erstellen, wenn die schueler davon noch nix kennen.

‚aufzäumen‘ :wink: