diff --git a/src/test/java/de/tla2b/util/AbstractParseModuleTest.java b/src/test/java/de/tla2b/util/AbstractParseModuleTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..f30bbdb7f17abad6510b26ae11e5d9935407e329
--- /dev/null
+++ b/src/test/java/de/tla2b/util/AbstractParseModuleTest.java
@@ -0,0 +1,107 @@
+package de.tla2b.util;
+
+import java.io.File;
+import java.io.FilenameFilter;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+
+import de.tla2b.util.PolySuite.Configuration;
+
+
+public abstract class AbstractParseModuleTest {
+	private static final String[] SUFFIX = { ".tla" };
+	
+	private static final class ModuleFilenameFilter implements FilenameFilter {
+	
+
+		public boolean accept(final File dir, final String name) {
+			for (int i = 0; i < SUFFIX.length; i++) {
+				if (name.endsWith(SUFFIX[i])) {
+					return true;
+				}
+			}
+			return false;
+		}
+	}
+
+	protected static File[] getModules(String path) {
+		final File dir = new File(path);
+		return dir.listFiles(new ModuleFilenameFilter());
+	}
+
+	protected static File[] getModulesRecursively(String path, ArrayList<String> ignoreList) {
+		File[] files = walk(path, ignoreList).toArray(new File[0]);
+		return files;
+	}
+
+	private static ArrayList<File> walk(String path, ArrayList<String> ignoreList) {
+		File root = new File(path);
+		File[] list = root.listFiles();
+		
+		ArrayList<File> files = new ArrayList<File>();
+		if (list == null)
+			return files;
+
+		for (File f : list) {
+			if (f.isDirectory()) {
+				boolean visitDir = true;
+				for (String string : ignoreList) {
+					File ignore = new File(string);
+					try {
+						if(f.getCanonicalPath().equals(ignore.getCanonicalPath())){
+							visitDir = false;
+						}
+					} catch (IOException e) {
+						visitDir = false;
+					}
+				}
+				if(visitDir){
+					files.addAll(walk(f.getAbsolutePath(),ignoreList));
+				}
+				
+			} else {
+				String name =f.getName();
+					for (int i = 0; i < SUFFIX.length; i++) {
+						if (name.endsWith(SUFFIX[i])) {
+							files.add(f);
+						}
+					}
+			}
+		}
+		return files;
+	}
+
+	protected static Configuration getConfiguration2(ArrayList<String> list, ArrayList<String> ignoreList) {
+		final ArrayList<File> allModules = new ArrayList<File>();
+
+		final ArrayList<Object> expectedValues = new ArrayList<Object>();
+		for (String path : list) {
+			File[] modules = getModulesRecursively(path, ignoreList);
+			allModules.addAll(Arrays.asList(modules));
+			for (int i = 0; i < modules.length; i++) {
+				expectedValues.add(1);
+			}
+		}
+
+		return new Configuration() {
+			public int size() {
+				return allModules.size();
+			}
+
+			public File getTestValue(int index) {
+				return allModules.get(index);
+			}
+
+			public String getTestName(int index) {
+				return allModules.get(index).getName();
+			}
+
+			public Object getExpectedValue(int index) {
+				return expectedValues.get(index);
+			}
+		};
+	}
+
+
+}
\ No newline at end of file
diff --git a/src/test/java/de/tla2b/util/PolySuite.java b/src/test/java/de/tla2b/util/PolySuite.java
new file mode 100644
index 0000000000000000000000000000000000000000..328d0c506f242810c68d0e6a83d87ce740f2eace
--- /dev/null
+++ b/src/test/java/de/tla2b/util/PolySuite.java
@@ -0,0 +1,140 @@
+package de.tla2b.util;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import org.junit.runner.Runner;
+import org.junit.runner.notification.RunNotifier;
+import org.junit.runners.BlockJUnit4ClassRunner;
+import org.junit.runners.Suite;
+import org.junit.runners.model.FrameworkMethod;
+import org.junit.runners.model.InitializationError;
+import org.junit.runners.model.Statement;
+import org.junit.runners.model.TestClass;
+
+public class PolySuite extends Suite {
+
+	  // //////////////////////////////
+	  // Public helper interfaces
+
+	  /**
+	   * Annotation for a method which returns a {@link Configuration}
+	   * to be injected into the test class constructor
+	   */
+	  @Retention(RetentionPolicy.RUNTIME)
+	  @Target(ElementType.METHOD)
+	  public static @interface Config {
+	  }
+
+	  public static interface Configuration {
+	    int size();
+	    Object getTestValue(int index);
+	    Object getExpectedValue(int index);
+	    String getTestName(int index);
+	  }
+
+	  // //////////////////////////////
+	  // Fields
+
+	  private final List<Runner> runners;
+
+	  // //////////////////////////////
+	  // Constructor
+
+	  /**
+	   * Only called reflectively. Do not use programmatically.
+	   * @param c the test class
+	   * @throws Throwable if something bad happens
+	   */
+	  public PolySuite(Class<?> c) throws Throwable {
+	    super(c, Collections.<Runner>emptyList());
+	    TestClass testClass = getTestClass();
+	    Class<?> jTestClass = testClass.getJavaClass();
+	    Configuration configuration = getConfiguration(testClass);
+	    List<Runner> runners = new ArrayList<Runner>();
+	    for (int i = 0, size = configuration.size(); i < size; i++) {
+	      SingleRunner runner = new SingleRunner(jTestClass, configuration.getTestValue(i), configuration.getTestName(i), configuration.getExpectedValue(i));
+	      runners.add(runner);
+	    }
+	    this.runners = runners;
+	  }
+
+	  // //////////////////////////////
+	  // Overrides
+
+	  @Override
+	  protected List<Runner> getChildren() {
+	    return runners;
+	  }
+
+	  // //////////////////////////////
+	  // Private
+
+	  private Configuration getConfiguration(TestClass testClass) throws Throwable {
+	    return (Configuration) getConfigMethod(testClass).invokeExplosively(null);
+	  }
+
+	  private FrameworkMethod getConfigMethod(TestClass testClass) {
+	    List<FrameworkMethod> methods = testClass.getAnnotatedMethods(Config.class);
+	    if (methods.isEmpty()) {
+	      throw new IllegalStateException("@" + Config.class.getSimpleName() + " method not found");
+	    }
+	    if (methods.size() > 1) {
+	      throw new IllegalStateException("Too many @" + Config.class.getSimpleName() + " methods");
+	    }
+	    FrameworkMethod method = methods.get(0);
+	    int modifiers = method.getMethod().getModifiers();
+	    if (!(Modifier.isStatic(modifiers) && Modifier.isPublic(modifiers))) {
+	      throw new IllegalStateException("@" + Config.class.getSimpleName() + " method \"" + method.getName() + "\" must be public static");
+	    }
+	    return method;
+	  }
+
+	  // //////////////////////////////
+	  // Helper classes
+
+	  private static class SingleRunner extends BlockJUnit4ClassRunner {
+
+	    private final Object testVal;
+	    private final Object expectedVal;
+	    private final String testName;
+
+	    SingleRunner(Class<?> testClass, Object testVal, String testName, Object expectedVal) throws InitializationError {
+	      super(testClass);
+	      this.testVal = testVal;
+	      this.expectedVal = expectedVal;
+	      this.testName = testName;
+	    }
+
+	    @Override
+	    protected Object createTest() throws Exception {
+	      return getTestClass().getOnlyConstructor().newInstance(testVal, expectedVal);
+	    }
+
+	    @Override
+	    protected String getName() {
+	      return testName;
+	    }
+
+	    @Override
+	    protected String testName(FrameworkMethod method) {
+	      return testName + ": " + method.getName();
+	    }
+
+	    @Override
+	    protected void validateConstructor(List<Throwable> errors) {
+	      validateOnlyOneConstructor(errors);
+	    }
+
+	    @Override
+	    protected Statement classBlock(RunNotifier notifier) {
+	      return childrenInvoker(notifier);
+	    }
+	  }
+	}
\ No newline at end of file