# Maven: mit JUnit auf Test- und Main-Classes zugreifen



## jazzschmidt (10. Aug 2012)

Hallo Leute!

Ich habe ein Maven Projekt, dass sich in *main* und *test* spaltet. Ich habe einen PackageImporter geschrieben, der per Reflection Klassen durchsucht und die public Methoden auf Annotationen prüft.
In einem Test (in *test*) rufe ich den Importer auf, der sich in *main* befindet. Dadurch kann ich außerhalb von *test* kein Package importieren. Was kann ich verändern, um es zum Laufen zu bringen?
Hier der Quelltext des PackageImporters:

```
package de.engram.test;


import java.io.File;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;

@SuppressWarnings("rawtypes")
public class PackageImporter {

	private final String packageName;
	private final boolean recursive;
	private final String ignoreClassesRegEx;

	/**
	 * Neuen PackageImporter erstellen. Durchsucht das Package rekursiv, wenn <code>recursive</code>
	 * auf <code>true</code> gesetzt ist. <code>ignoreClassesRegEx</code> wird angegeben, um mit
	 * einem regulären Ausdruck Klassen zu ignorieren.
	 * <p>
	 * <b>Beispiel:</b><br />
	 * <code>
	 * String ignoreClassesRegEx = "Test{1}.*";<br />
	 * PackageImporter pi = new PackageImporter("de.foo.bar", true, regEx);<p />
	 * </code> Würde alle Klassen, die nicht mit "Test" beginnen aus dem Package <i>de.foo.bar</i>
	 * und der Subpackages einlesen.
	 * <p>
	 * 
	 * @param packageName der Name des Pakets, dessen Klassen eingelesen werden sollen
	 * @param ignoreClassesRegEx Klassen, die diesem regulären Ausdruck entsprechen, werden von der
	 *            Prüfung ausgenommen
	 * @param recursive gibt an, ob das Paket rekursiv durchsucht werden soll
	 */
	public PackageImporter(final String packageName, final boolean recursive, final String ignoreClassesRegEx) {
		this.packageName = packageName;
		this.recursive = recursive;
		this.ignoreClassesRegEx = ignoreClassesRegEx;
	}

	/**
	 * Neuen PackageImporter erstellen. Durchsucht das Package rekursiv, wenn <code>recursive</code>
	 * auf <code>true</code> gesetzt ist.
	 * 
	 * @see #PackageImporter(String, boolean, String)
	 * 
	 * @param packageName der Name des Pakets, dessen Klassen eingelesen werden sollen
	 * @param recursive gibt an, ob das Paket rekursiv durchsucht werden soll
	 */
	public PackageImporter(final String packageName, final boolean recursive) {
		this(packageName, recursive, "");
	}

	/**
	 * Neuen PackageImporter erstellen. Durchsucht das Package <i>nicht</i> rekursiv.
	 * 
	 * @see #PackageImporter(String, boolean, String)
	 * 
	 * @param packageName der Name des Pakets, dessen Klassen eingelesen werden sollen
	 */
	public PackageImporter(final String packageName) {
		this(packageName, false, "");
	}

	/**
	 * Importiert alle Klassen.
	 * 
	 * @return die importierten Klassen
	 */
	public List<Class> importClasses() {
		final ClassLoader classLoader = Thread.currentThread().getContextClassLoader();

		String path = packageName.replace(".", "/");
		final URL resources = classLoader.getResource(path);
		path = resources.getPath();

		final List<Class> classes = new ArrayList<Class>();
		importClassesFromDir(classes, path, 0);

		return classes;
	}

	/**
	 * Importiert rekursiv Klassen aus dem übergebenen Pfad.
	 * 
	 * @param classes die zu füllende Liste der Klassen
	 * @param path der Pfad
	 * @param directoryDepth die Pfadtiefe
	 */
	private void importClassesFromDir(final List<Class> classes, final String path, final int directoryDepth) {
		final File importDir = new File(path);
		final File[] files = importDir.listFiles();
		for (final File file : files) {
			final String absolutePath = file.getAbsolutePath();
			final int dot = absolutePath.lastIndexOf('.');
			final String extension = absolutePath.substring(dot + 1);

			if (file.isDirectory() && recursive) {
				importClassesFromDir(classes, absolutePath, directoryDepth + 1);
			} else if (file.isFile() && !file.getName().matches(ignoreClassesRegEx) && "class".equals(extension)) {
				final String packagePath = file.getParent().replace(File.separator, ".");

				String subPackage = "";
				final String fileName = file.getName();

				int index;
				int found = 0;
				char character;
				for (index = packagePath.length() - 1; found < directoryDepth; index--) {
					character = packagePath.charAt(index);
					if (character == '.') {
						found++;
					}

					subPackage = packagePath.substring(index + 1) + ".";
				}

				String filePath;
				if (directoryDepth > 0) {
					filePath = subPackage + fileName;
				} else {
					filePath = fileName;
				}

				classes.add(importFile(filePath));
			}
		}
	}

	/**
	 * Importiert die Klasse mit dem übergebenen Namen.
	 * 
	 * @param className der Klassenname
	 * @return die Klasse
	 */
	private Class importFile(final String className) {
		System.out.println("Importing " + className);
		Class clazz = null;
		try {
			clazz = Class.forName(packageName + "." + className.substring(0, className.length() - 6));
		}
		catch (final ClassNotFoundException e) {
			// In eine RuntimeException umwandeln, damit wir uns nicht kümmern müssen
			throw new RuntimeException(e.getMessage(), e);
		}
		System.out.println(clazz.getName() + " imported.");
		return clazz;
	}
}
```

Danke im Vorraus für jegliche Hilfe!


----------



## Noctarius (10. Aug 2012)

Was soll das bringen? Wieso solltest du nicht an andere Packages rankommen und was soll der Test eigentlich testen?


----------



## hexx (10. Aug 2012)

Du gehst davon aus, dass es nur eine Ressource pro Pfad gibt:


```
final URL resources = classLoader.getResource(path);
```

classLoader.getResource*s*(path) sollte helfen.




```
final File importDir = new File(path);
```

und was machst du, wenn die Dateien in einem Jar liegen (ist ja nicht ganz unüblich)?


----------



## jazzschmidt (13. Aug 2012)

*@Noctarius*
Der Test überprüft alle Klassen eines Packages auf richtig annotierte Methoden. Das sind Datenbankzugriffe, die teilweise mit _transactional_ gekennzeichnet sind und welche, die explizit als nicht-transactional gekennzeichnet sein müssen. Ist eine Methode nicht gekennzeichnet, schlägt der Test fehl und stoppt den Build-Vorgang.

*@hexx*
Dankesehr! Das war die Lösung.
Die Tests werden nur für den Build-Vorgang benötigt und kommen nicht mit ins jar.


----------

