diff --git a/dumpAst/src/main/jastadd/DumpAst.relast b/dumpAst/src/main/jastadd/DumpAst.relast
index e7acd5daa36734376fa0858fbd1cd026b857f152..f1b76ce06860a121b868cdb47844d2fc33c70083 100644
--- a/dumpAst/src/main/jastadd/DumpAst.relast
+++ b/dumpAst/src/main/jastadd/DumpAst.relast
@@ -1,5 +1,5 @@
 DumpAst ::= DumpNode* <PackageName> BuildConfig PrintConfig ;
-BuildConfig ::= <TypeIgnore> <TokenIgnore> <ChildIgnore> <AttributeIgnore> <RelationIgnore> <IgnoreEmptyString:boolean> <Debug:boolean> ;
+BuildConfig ::= <TypeIgnore> <TokenIgnore> <ChildIgnore> <AttributeIgnore> <RelationIgnore> <IncludeEmptyString:boolean> <Debug:boolean> ;
 PrintConfig ::= <Scale:double> <Version> Header* ;
 Header ::= <Value> ;
 DumpNode ::= <Name> <Label> <Object:Object> DumpChildNode* DumpToken* DumpRelation* ;
diff --git a/dumpAst/src/main/jastadd/Generation.jadd b/dumpAst/src/main/jastadd/Generation.jadd
index 692220c3af5bec01d169da9279466d8961f85438..06a8d254b133ff843108a6c02602b19167e1d282 100644
--- a/dumpAst/src/main/jastadd/Generation.jadd
+++ b/dumpAst/src/main/jastadd/Generation.jadd
@@ -1,13 +1,24 @@
-aspect GenerationFrontend {
+import java.lang.String;aspect GenerationFrontend {
   public class Dumper {
+    /**
+     * Prepare to read in the given object. Use the <code>dump*</code> methods to actually dump its content.
+     * @param obj the object to dump
+     * @return a builder to adjust dump options
+     */
     public static DumpBuilder read(Object obj) {
       return new DumpBuilder(obj);
     }
   }
   public enum SkinParamBooleanSetting {
-    Monochrome, Shadowing, Handwritten
+    /** Print in grayscale? */
+    Monochrome,
+    /** Use shadows? */
+    Shadowing,
+    /** Use handwritten style? */
+    Handwritten
   }
   public enum SkinParamStringSetting {
+    /** Set color of background */
     backgroundColor
   }
   public class DumpBuilder {
@@ -21,34 +32,49 @@ aspect GenerationFrontend {
       this.target = target;
       printConfig.setScale(1);
       printConfig.setVersion(readVersion());
-      ignoreEmptyStrings(true);
     }
 
-    public DumpBuilder setDebug(boolean debug) {
-      buildConfig.setDebug(debug);
+    /**
+     * Add debug information in dumped content, mainly version numbers.
+     * @return this
+     */
+    public DumpBuilder enableDebug() {
+      buildConfig.setDebug(true);
       return this;
     }
-    public DumpBuilder ignoreEmptyStrings(boolean ignoreThem) {
-      buildConfig.setIgnoreEmptyString(ignoreThem);
+
+    /**
+     * Include empty strings for all tokens
+     * @return this
+     */
+    public DumpBuilder includeEmptyStringsOnTokens() {
+      buildConfig.setIncludeEmptyString(true);
       return this;
     }
-    public DumpBuilder ignoreTypes(String... regexes) {
+
+    /**
+     * Exclude object with types matching at least one of the given regex strings.
+     * @param regexes patterns to match type names
+     * @return this
+     * @see java.util.regex.Pattern#compile(java.lang.String)
+     */
+    public DumpBuilder excludeTypes(String... regexes) {
       updateIgnored(() -> buildConfig.getTypeIgnore(), s -> buildConfig.setTypeIgnore(s), regexes);
       return this;
     }
-    public DumpBuilder ignoreTokens(String... regexes) {
+    public DumpBuilder excludeTokens(String... regexes) {
       updateIgnored(() -> buildConfig.getTokenIgnore(), s -> buildConfig.setTokenIgnore(s), regexes);
       return this;
     }
-    public DumpBuilder ignoreAttributes(String... regexes) {
+    public DumpBuilder excludeAttributes(String... regexes) {
       updateIgnored(() -> buildConfig.getAttributeIgnore(), s -> buildConfig.setAttributeIgnore(s), regexes);
       return this;
     }
-    public DumpBuilder ignoreChildren(String... regexes) {
+    public DumpBuilder excludeChildren(String... regexes) {
       updateIgnored(() -> buildConfig.getChildIgnore(), s -> buildConfig.setChildIgnore(s), regexes);
       return this;
     }
-    public DumpBuilder ignoreRelations(String... regexes) {
+    public DumpBuilder excludeRelations(String... regexes) {
       updateIgnored(() -> buildConfig.getRelationIgnore(), s -> buildConfig.setRelationIgnore(s), regexes);
       return this;
     }
@@ -221,7 +247,7 @@ aspect GenerationBackend {
           if (targetNode != null && targetNode.isAstNode()) {
             token = new DumpReferenceToken().setValue(targetNode);
           } else {
-            if (target != null && (!getBuildConfig().getIgnoreEmptyString() || !target.toString().isEmpty())) {
+            if (target != null && (getBuildConfig().getIncludeEmptyString() || !target.toString().isEmpty())) {
               DumpValueToken valueToken = new DumpValueToken();
               valueToken.setValue(target);
               token = valueToken;
@@ -312,7 +338,7 @@ aspect GenerationBackend {
             case "Attribute":
               // TODO. check for isNTA=true. then check for whether target is instance of iterable. if so, add to listChildren, otherwise to singelChild
               // TODO. if isNTA=false, then handle attribute
-              break
+              break;
           }
         }
       }
diff --git a/dumpAst/src/main/java/de/tudresden/inf/st/jastadd/dumpAst/SimpleMain.java b/dumpAst/src/main/java/de/tudresden/inf/st/jastadd/dumpAst/SimpleMain.java
index 812d1a1881aa84804f3fe28fc4852de64ab48056..0d1b70cc6eb18ad6f9d4b75149ebaedcf206481b 100644
--- a/dumpAst/src/main/java/de/tudresden/inf/st/jastadd/dumpAst/SimpleMain.java
+++ b/dumpAst/src/main/java/de/tudresden/inf/st/jastadd/dumpAst/SimpleMain.java
@@ -115,8 +115,8 @@ public class SimpleMain {
 //    traverseInitial(model.toMustache());
     Dumper.read(model.toMustache())
         .skinParam(SkinParamBooleanSetting.Monochrome, true)
-        .setDebug(true)
-        .ignoreTypes(".*Comment")
+        .enableDebug()
+        .excludeTypes(".*Comment")
         .dumpAsSource(Paths.get("temp.plantuml"))
         .dumpAsSVG(Paths.get("temp.svg"));
   }
@@ -143,7 +143,7 @@ public class SimpleMain {
     relation.addComment(comment);
 
     Dumper.read(program)
-        .ignoreTokens("Name")
+        .excludeTokens("Name")
 //        .ignoreTypes(".*Comment")
         .dumpAsSource(Paths.get("grammar.plantuml"))
         .dumpAsSVG(Paths.get("grammar.svg"));
diff --git a/testDumper/src/main/jastadd/testDumper.relast b/testDumper/src/main/jastadd/testDumper.relast
index c830eb955d70f390aa57f6fddb1982611a73d58c..b49a389c197d953eb47019e7759300ee94d9aa0e 100644
--- a/testDumper/src/main/jastadd/testDumper.relast
+++ b/testDumper/src/main/jastadd/testDumper.relast
@@ -1,8 +1,8 @@
 Nameable ::= <Name> ;
 Root : Nameable ::= A B* [C] ;
-A : Nameable ::= B C ;
-B : Nameable ::= ;
-C : Nameable ::= <Unwanted:int> <RawReference:A> /Calculated:A/ /AlsoCalculated:B*/ ;
+A : Nameable ::= B MyC:C ;
+B : Nameable ::= <OtherValue> ;
+C : Nameable ::= [A] <Unwanted:int> <RawReference:A> /Calculated:A/ /AlsoCalculated:B*/ ;
 
 rel B.oneA -> A ;
 rel B.maybeC? -> C ;
diff --git a/testDumper/src/test/java/de/tudresden/inf/st/jastadd/testDumper/TestExcluded.java b/testDumper/src/test/java/de/tudresden/inf/st/jastadd/testDumper/TestExcluded.java
new file mode 100644
index 0000000000000000000000000000000000000000..16fc3b69aeda5496b62cfcb18f59540ad42c49e9
--- /dev/null
+++ b/testDumper/src/test/java/de/tudresden/inf/st/jastadd/testDumper/TestExcluded.java
@@ -0,0 +1,150 @@
+package de.tudresden.inf.st.jastadd.testDumper;
+
+import de.tudresden.inf.st.jastadd.dumpAst.ast.DumpBuilder;
+import de.tudresden.inf.st.jastadd.dumpAst.ast.DumpNode;
+import org.jastadd.testDumper.ast.Root;
+import org.junit.jupiter.api.Test;
+
+import java.util.List;
+
+import static de.tudresden.inf.st.jastadd.testDumper.TestUtils.*;
+import static org.assertj.core.api.Assertions.*;
+
+public class TestExcluded {
+
+  @Test
+  public void testEmptyStringsOnTokensDefault() {
+    Root root = createRoot(null, null, createB(B1_NAME, "something"), createB(B2_NAME), createB(B3_NAME));
+
+    // normal mode, do not include empty strings
+    List<DumpNode> nodes = TestUtils.dumpModel(root);
+    assertThat(nodes).flatExtracting(NAME_EXTRACTOR).containsExactlyInAnyOrder(ROOT_NAME, B1_NAME, B2_NAME, B3_NAME);
+    DumpNode actualB1 = TestUtils.findByName(nodes, B1_NAME);
+    assertThat(valueTokens(actualB1)).containsOnly(entry("Name", B1_NAME), entry(TOKEN_LABEL_OTHER_VALUE, "something"));
+    DumpNode actualB2 = TestUtils.findByName(nodes, B2_NAME);
+    assertThat(valueTokens(actualB2)).containsOnly(entry("Name", B2_NAME));
+    DumpNode actualB3 = TestUtils.findByName(nodes, B3_NAME);
+    assertThat(valueTokens(actualB3)).containsOnly(entry("Name", B3_NAME));
+  }
+
+  @Test
+  public void testEmptyStringsOnTokensIncluded() {
+    Root root = createRoot(null, null, createB(B1_NAME, "something"), createB(B2_NAME), createB(B3_NAME));
+
+    // test mode, do include empty strings
+    List<DumpNode> nodes = TestUtils.dumpModel(root, DumpBuilder::includeEmptyStringsOnTokens);
+    assertThat(nodes).flatExtracting(NAME_EXTRACTOR).containsExactlyInAnyOrder(ROOT_NAME, B1_NAME, B2_NAME, B3_NAME);
+    DumpNode actualB1 = TestUtils.findByName(nodes, B1_NAME);
+    assertThat(valueTokens(actualB1)).containsOnly(entry("Name", B1_NAME), entry(TOKEN_LABEL_OTHER_VALUE, "something"));
+    DumpNode actualB2 = TestUtils.findByName(nodes, B2_NAME);
+    assertThat(valueTokens(actualB2)).containsOnly(entry("Name", B2_NAME), entry(TOKEN_LABEL_OTHER_VALUE, ""));
+    DumpNode actualB3 = TestUtils.findByName(nodes, B3_NAME);
+    assertThat(valueTokens(actualB3)).containsOnly(entry("Name", B3_NAME), entry(TOKEN_LABEL_OTHER_VALUE, ""));
+  }
+
+  private Root setupTypes() {
+    Root root = createRoot(createA(A_NAME), createC(C_NAME), createB(B_NAME, "A"));
+    root.getC().setRawReference(root.getA());
+    // rel B.oneA -> A ;
+    root.getB(0).setOneA(root.getA());
+    // rel B.maybeC? -> C ;
+    root.getB(0).addManyA(root.getA());
+    // rel B.manyA* -> A ;
+    root.getB(0).setMaybeC(root.getC());
+    // rel C.biA1 <-> A.biC1 ;
+    root.getC().setBiA1(root.getA());
+    // rel C.biA2* <-> A.biC2 ;
+    root.getC().addBiA2(root.getA());
+    // rel C.biA3? <-> A.biC3 ;
+    root.getC().setBiA3(root.getA());
+    return root;
+  }
+
+  @Test
+  public void testTypesDefault() {
+    Root root = setupTypes();
+
+    List<DumpNode> nodes = TestUtils.dumpModel(root);
+    assertTypeDefaultMatches(nodes);
+  }
+
+  private void assertTypeDefaultMatches(List<DumpNode> nodes) {
+    assertThat(nodes).flatExtracting(NAME_EXTRACTOR).containsExactlyInAnyOrder(ROOT_NAME, A_NAME, B_NAME, C_NAME);
+    // A
+    assertThatMapOf(normalRelationChildren(findByName(nodes, A_NAME))).containsExactlyInAnyOrder(
+        tuple("BiC1", C_NAME), tuple("BiC2", C_NAME), tuple("BiC3", C_NAME));
+    // B
+    assertThatMapOf(normalRelationChildren(findByName(nodes, B_NAME))).containsExactlyInAnyOrder(
+        tuple("OneA", A_NAME), tuple("MaybeC", C_NAME));
+    // C
+    assertThatMapOf(normalRelationChildren(findByName(nodes, C_NAME))).containsExactlyInAnyOrder(
+        tuple("BiA1", A_NAME), tuple("BiA3", A_NAME));
+    assertThatMapOf(listRelationChildren(findByName(nodes, C_NAME)), "BiA2").containsExactlyInAnyOrder(A_NAME);
+    assertThatMapOf(referenceTokens(findByName(nodes, C_NAME))).containsExactlyInAnyOrder(
+        tuple("RawReference", A_NAME));
+  }
+
+  @Test
+  public void testTypesNonMatching() {
+    Root root = setupTypes();
+
+    List<DumpNode> nodes = TestUtils.dumpModel(root, db -> db.excludeTypes("NonExistingType"));
+    assertTypeDefaultMatches(nodes);
+  }
+
+  @Test
+  public void testTypesInheritanceNotIncluded() {
+    Root root = setupTypes();
+
+    List<DumpNode> nodes = TestUtils.dumpModel(root, db -> db.excludeTypes("Nameable"));
+    assertTypeDefaultMatches(nodes);
+  }
+
+  @Test
+  public void testTypesExcludeOnlyA() {
+    Root root = setupTypes();
+
+    List<DumpNode> nodes = TestUtils.dumpModel(root, db -> db.excludeTypes("A"));
+    assertThat(nodes).flatExtracting(NAME_EXTRACTOR).containsExactlyInAnyOrder(ROOT_NAME, B_NAME, C_NAME);
+    // B
+    assertThatMapOf(normalRelationChildren(findByName(nodes, B_NAME))).containsExactlyInAnyOrder(
+        tuple("MaybeC", (C_NAME)));
+    // C
+    assertThat(normalRelationChildren(findByName(nodes, C_NAME))).isEmpty();
+    assertThat(listRelationChildren(findByName(nodes, C_NAME))).isEmpty();
+    assertThat(referenceTokens(findByName(nodes, C_NAME))).isEmpty();
+  }
+
+  @Test
+  public void testTypesExcludeMultipleArguments() {
+    Root root = setupTypes();
+
+    List<DumpNode> nodes = TestUtils.dumpModel(root, db -> db.excludeTypes("A", "C"));
+    assertThat(nodes).flatExtracting(NAME_EXTRACTOR).containsExactlyInAnyOrder(ROOT_NAME, B_NAME);
+    // B
+    assertThatMapOf(normalRelationChildren(findByName(nodes, B_NAME))).isEmpty();
+  }
+
+  @Test
+  public void testTypesExcludeMultipleCalls() {
+    Root root = setupTypes();
+
+    List<DumpNode> nodes = TestUtils.dumpModel(root, db -> db.excludeTypes("A").excludeTypes("C"));
+    assertThat(nodes).flatExtracting(NAME_EXTRACTOR).containsExactlyInAnyOrder(ROOT_NAME, B_NAME);
+    // B
+    assertThatMapOf(normalRelationChildren(findByName(nodes, B_NAME))).isEmpty();
+  }
+
+  // --- copy from below here ---
+
+  @Test
+  public void testXDefault() {
+
+  }
+
+  @Test
+  public void testXExclude() {
+
+  }
+
+}
diff --git a/testDumper/src/test/java/de/tudresden/inf/st/jastadd/testDumper/TestSimple.java b/testDumper/src/test/java/de/tudresden/inf/st/jastadd/testDumper/TestSimple.java
index de89eeb5a2a8be82fd354480163dadc5c7955cc8..4dfb16c46f5ffcea4c75da01183e53cbea6a072d 100644
--- a/testDumper/src/test/java/de/tudresden/inf/st/jastadd/testDumper/TestSimple.java
+++ b/testDumper/src/test/java/de/tudresden/inf/st/jastadd/testDumper/TestSimple.java
@@ -1,9 +1,6 @@
 package de.tudresden.inf.st.jastadd.testDumper;
 
 import de.tudresden.inf.st.jastadd.dumpAst.ast.DumpNode;
-import org.jastadd.testDumper.ast.A;
-import org.jastadd.testDumper.ast.B;
-import org.jastadd.testDumper.ast.C;
 import org.jastadd.testDumper.ast.Root;
 import org.junit.jupiter.api.Test;
 
@@ -11,14 +8,14 @@ import java.util.List;
 
 import static de.tudresden.inf.st.jastadd.testDumper.TestUtils.*;
 import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.tuple;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 
 public class TestSimple {
 
   @Test
   public void testEmpty() {
-    Root root = new Root();
-    root.setName(ROOT_NAME);
+    Root root = createRoot(null, null);
 
     List<DumpNode> nodes = TestUtils.dumpModel(root);
     assertThat(nodes).flatExtracting(NAME_EXTRACTOR).containsExactly(ROOT_NAME);
@@ -30,11 +27,7 @@ public class TestSimple {
 
   @Test
   public void testOneNormalChild() {
-    Root root = new Root();
-    root.setName(ROOT_NAME);
-    A a = new A();
-    a.setName(A_NAME);
-    root.setA(a);
+    Root root = createRoot(createA(A_NAME), null);
 
     List<DumpNode> nodes = TestUtils.dumpModel(root);
     assertEquals(2, nodes.size());
@@ -43,36 +36,25 @@ public class TestSimple {
     assertEquals(1, actualRoot.getNumDumpToken());
     assertEquals(1, actualRoot.getNumDumpChildNode());
     assertEquals(0, actualRoot.getNumDumpRelation());
-    List<DumpNode> children = TestUtils.normalChildren(actualRoot);
-    assertThat(children).flatExtracting(NAME_EXTRACTOR).containsExactlyInAnyOrder(A_NAME);
+    assertThatMapOf(normalChildren(actualRoot)).containsExactlyInAnyOrder(tuple("A", A_NAME));
   }
 
   @Test
-  public void testOneListChild() {
-    Root root = new Root();
-    root.setName(ROOT_NAME);
-    B b = new B();
-    b.setName(B_NAME);
-    root.addB(b);
+  public void testListChildren() {
+    Root root = createRoot(null, null, createB(B1_NAME), createB(B2_NAME), createB(B3_NAME));
 
     List<DumpNode> nodes = TestUtils.dumpModel(root);
-    assertEquals(2, nodes.size());
-    assertThat(nodes).flatExtracting(NAME_EXTRACTOR).containsExactlyInAnyOrder(ROOT_NAME, B_NAME);
+    assertThat(nodes).flatExtracting(NAME_EXTRACTOR).containsExactlyInAnyOrder(ROOT_NAME, B1_NAME, B2_NAME, B3_NAME);
     DumpNode actualRoot = TestUtils.findByName(nodes, ROOT_NAME);
     assertEquals(1, actualRoot.getNumDumpToken());
     assertEquals(1, actualRoot.getNumDumpChildNode());
     assertEquals(0, actualRoot.getNumDumpRelation());
-    List<DumpNode> children = TestUtils.listChildren(actualRoot);
-    assertThat(children).flatExtracting(NAME_EXTRACTOR).containsExactlyInAnyOrder(B_NAME);
+    assertThatMapOf(listChildren(actualRoot), "B").containsExactlyInAnyOrder(B1_NAME, B2_NAME, B3_NAME);
   }
 
   @Test
   public void testOneOptChild() {
-    Root root = new Root();
-    root.setName(ROOT_NAME);
-    C c = new C();
-    c.setName(C_NAME);
-    root.setC(c);
+    Root root = createRoot(null, createC(C_NAME));
 
     List<DumpNode> nodes = TestUtils.dumpModel(root);
     assertEquals(2, nodes.size());
@@ -81,122 +63,98 @@ public class TestSimple {
     assertEquals(1, actualRoot.getNumDumpToken());
     assertEquals(1, actualRoot.getNumDumpChildNode());
     assertEquals(0, actualRoot.getNumDumpRelation());
-    List<DumpNode> children = TestUtils.normalChildren(actualRoot);
-    assertThat(children).flatExtracting(NAME_EXTRACTOR).containsExactlyInAnyOrder(C_NAME);
+    assertThatMapOf(normalChildren(actualRoot)).containsExactlyInAnyOrder(tuple("C", C_NAME));
   }
 
   @Test
   public void testNormalUniRelation() {
-    Root root = relationSetup();
+    Root root = createRoot(createA(A_NAME), createC(C_NAME), createB(B_NAME));
     root.getB(0).setOneA(root.getA());
 
     List<DumpNode> nodes = TestUtils.dumpModel(root);
     assertEquals(4, nodes.size());
     DumpNode actualB = TestUtils.findByName(nodes, B_NAME);
-    assertEquals(1, actualB.getNumDumpRelation());
-    assertEquals("OneA", actualB.getDumpRelation(0).label());
-    List<DumpNode> relationEnds = normalRelationChildren(actualB);
-    assertThat(relationEnds).flatExtracting(NAME_EXTRACTOR).containsExactlyInAnyOrder(A_NAME);
+    assertThatMapOf(normalRelationChildren(actualB)).containsExactlyInAnyOrder(tuple("OneA", A_NAME));
   }
 
   @Test
   public void testListUniRelation() {
-    Root root = relationSetup();
+    final String A2_NAME = "anotherA";
+    Root root = createRoot(createA(A_NAME), createC(C_NAME), createB(B_NAME));
+    root.getC().setA(createA(A2_NAME));
     root.getB(0).addManyA(root.getA());
+    root.getB(0).addManyA(root.getC().getA());
 
     List<DumpNode> nodes = TestUtils.dumpModel(root);
-    assertEquals(4, nodes.size());
+    assertThat(nodes).flatExtracting(NAME_EXTRACTOR).containsExactlyInAnyOrder(ROOT_NAME, A_NAME, A2_NAME, B_NAME, C_NAME);
     DumpNode actualB = TestUtils.findByName(nodes, B_NAME);
-    assertEquals(1, actualB.getNumDumpRelation());
-    assertEquals("ManyA", actualB.getDumpRelation(0).label());
-    List<DumpNode> relationEnds = listRelationChildren(actualB);
-    assertThat(relationEnds).flatExtracting(NAME_EXTRACTOR).containsExactlyInAnyOrder(A_NAME);
+//    assertThatListMapOf(listRelationChildren(actualB)).containsExactlyInAnyOrder(
+//        tuple("ManyA", tuple(A_NAME, A2_NAME)));
+    assertThatMapOf(listRelationChildren(actualB), "ManyA").containsExactlyInAnyOrder(A_NAME, A2_NAME);
   }
 
   @Test
   public void testOptUniRelation() {
-    Root root = relationSetup();
+    Root root = createRoot(createA(A_NAME), createC(C_NAME), createB(B_NAME));
     root.getB(0).setMaybeC(root.getC());
 
     List<DumpNode> nodes = TestUtils.dumpModel(root);
     assertEquals(4, nodes.size());
     DumpNode actualB = TestUtils.findByName(nodes, B_NAME);
-    assertEquals(1, actualB.getNumDumpRelation());
-    assertEquals("MaybeC", actualB.getDumpRelation(0).label());
-    List<DumpNode> relationEnds = normalRelationChildren(actualB);
-    assertThat(relationEnds).flatExtracting(NAME_EXTRACTOR).containsExactlyInAnyOrder(C_NAME);
+    assertThatMapOf(normalRelationChildren(actualB)).containsExactlyInAnyOrder(tuple("MaybeC", C_NAME));
   }
 
   @Test
   public void testNormalBiRelation() {
-    Root root = relationSetup();
+    Root root = createRoot(createA(A_NAME), createC(C_NAME), createB(B_NAME));
     root.getC().setBiA1(root.getA());
 
     List<DumpNode> nodes = TestUtils.dumpModel(root);
     assertEquals(4, nodes.size());
     // bidirectional relations are currently not found, instead there will be two unidirectional ones
     DumpNode actualA = TestUtils.findByName(nodes, A_NAME);
-    assertEquals(1, actualA.getNumDumpRelation());
-    assertEquals("BiC1", actualA.getDumpRelation(0).label());
-    List<DumpNode> relationEndsOfA = normalRelationChildren(actualA);
-    assertThat(relationEndsOfA).flatExtracting(NAME_EXTRACTOR).containsExactlyInAnyOrder(C_NAME);
+    assertThatMapOf(normalRelationChildren(actualA)).containsExactlyInAnyOrder(tuple("BiC1", C_NAME));
     DumpNode actualC = TestUtils.findByName(nodes, C_NAME);
-    assertEquals(1, actualC.getNumDumpRelation());
-    assertEquals("BiA1", actualC.getDumpRelation(0).label());
-    List<DumpNode> relationEndsOfC = normalRelationChildren(actualC);
-    assertThat(relationEndsOfC).flatExtracting(NAME_EXTRACTOR).containsExactlyInAnyOrder(A_NAME);
+    assertThatMapOf(normalRelationChildren(actualC)).containsExactlyInAnyOrder(tuple("BiA1", A_NAME));
   }
 
   @Test
   public void testListBiRelation() {
-    Root root = relationSetup();
+    Root root = createRoot(createA(A_NAME), createC(C_NAME), createB(B_NAME));
     root.getC().addBiA2(root.getA());
 
     List<DumpNode> nodes = TestUtils.dumpModel(root);
     assertEquals(4, nodes.size());
     // bidirectional relations are currently not found, instead there will be two unidirectional ones
     DumpNode actualA = TestUtils.findByName(nodes, A_NAME);
-    assertEquals(1, actualA.getNumDumpRelation());
-    assertEquals("BiC2", actualA.getDumpRelation(0).label());
-    List<DumpNode> relationEndsOfA = normalRelationChildren(actualA);
-    assertThat(relationEndsOfA).flatExtracting(NAME_EXTRACTOR).containsExactlyInAnyOrder(C_NAME);
+    assertThatMapOf(normalRelationChildren(actualA)).containsExactlyInAnyOrder(tuple("BiC2", C_NAME));
     DumpNode actualC = TestUtils.findByName(nodes, C_NAME);
-    assertEquals(1, actualC.getNumDumpRelation());
-    assertEquals("BiA2", actualC.getDumpRelation(0).label());
-    List<DumpNode> relationEndsOfC = listRelationChildren(actualC);
-    assertThat(relationEndsOfC).flatExtracting(NAME_EXTRACTOR).containsExactlyInAnyOrder(A_NAME);
+    assertThatMapOf(listRelationChildren(actualC), "BiA2").containsExactlyInAnyOrder(A_NAME);
   }
 
   @Test
   public void testOptBiRelation() {
-    Root root = relationSetup();
+    Root root = createRoot(createA(A_NAME), createC(C_NAME), createB(B_NAME));
     root.getC().setBiA3(root.getA());
 
     List<DumpNode> nodes = TestUtils.dumpModel(root);
     assertEquals(4, nodes.size());
     // bidirectional relations are currently not found, instead there will be two unidirectional ones
     DumpNode actualA = TestUtils.findByName(nodes, A_NAME);
-    assertEquals(1, actualA.getNumDumpRelation());
-    assertEquals("BiC3", actualA.getDumpRelation(0).label());
-    List<DumpNode> relationEndsOfA = normalRelationChildren(actualA);
-    assertThat(relationEndsOfA).flatExtracting(NAME_EXTRACTOR).containsExactlyInAnyOrder(C_NAME);
+    assertThatMapOf(normalRelationChildren(actualA)).containsExactlyInAnyOrder(tuple("BiC3", C_NAME));
     DumpNode actualC = TestUtils.findByName(nodes, C_NAME);
-    assertEquals(1, actualC.getNumDumpRelation());
-    assertEquals("BiA3", actualC.getDumpRelation(0).label());
-    List<DumpNode> relationEndsOfC = normalRelationChildren(actualC);
-    assertThat(relationEndsOfC).flatExtracting(NAME_EXTRACTOR).containsExactlyInAnyOrder(A_NAME);
+    assertThatMapOf(normalRelationChildren(actualC)).containsExactlyInAnyOrder(tuple("BiA3", A_NAME));
   }
 
   @Test
   public void testOneNormalReferenceToken() {
-    Root root = relationSetup();
+    Root root = createRoot(createA(A_NAME), createC(C_NAME), createB(B_NAME));
     root.getC().setRawReference(root.getA());
 
     List<DumpNode> nodes = TestUtils.dumpModel(root);
     assertEquals(4, nodes.size());
     DumpNode actualC = TestUtils.findByName(nodes, C_NAME);
-    List<DumpNode> references = referenceTokens(actualC);
-    assertThat(references).flatExtracting(NAME_EXTRACTOR).containsExactlyInAnyOrder(A_NAME);
+    assertThatMapOf(referenceTokens(actualC)).containsExactlyInAnyOrder(tuple("RawReference", A_NAME));
   }
 
 }
diff --git a/testDumper/src/test/java/de/tudresden/inf/st/jastadd/testDumper/TestUtils.java b/testDumper/src/test/java/de/tudresden/inf/st/jastadd/testDumper/TestUtils.java
index 3bc908e0873254a7f76d27d7757d35e47bb4500a..f35e1cdfdbbcf7a5f6d990e51cfc0475386997f3 100644
--- a/testDumper/src/test/java/de/tudresden/inf/st/jastadd/testDumper/TestUtils.java
+++ b/testDumper/src/test/java/de/tudresden/inf/st/jastadd/testDumper/TestUtils.java
@@ -1,48 +1,96 @@
 package de.tudresden.inf.st.jastadd.testDumper;
 
 import de.tudresden.inf.st.jastadd.dumpAst.ast.*;
+import org.assertj.core.api.AbstractListAssert;
 import org.assertj.core.api.Assertions;
+import org.assertj.core.api.ObjectAssert;
+import org.assertj.core.groups.Tuple;
 import org.jastadd.testDumper.ast.A;
 import org.jastadd.testDumper.ast.B;
 import org.jastadd.testDumper.ast.C;
 import org.jastadd.testDumper.ast.Root;
 
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Random;
+import java.util.*;
 import java.util.function.Consumer;
 import java.util.function.Function;
 
-public class TestUtils {
-  static class ExposingDumpBuilder extends DumpBuilder {
+import static org.assertj.core.api.Assertions.tuple;
 
-    protected ExposingDumpBuilder(Object target) {
-      super(target);
-    }
+public class TestUtils {
+  public static final Random rand = new Random();
 
-    @Override
-    public DumpAst build() {
-      return super.build();
-    }
-  }
+  public static final String TOKEN_LABEL_OTHER_VALUE = "OtherValue";
 
-  public static final Random rand = new Random();
+  public static final String ROOT_NAME = "Root" + Integer.toHexString(rand.nextInt(0xFFFFFF));
   public static final String A_NAME = "A" + Integer.toHexString(rand.nextInt(0xFFFFFF));
   public static final String B_NAME = "B" + Integer.toHexString(rand.nextInt(0xFFFFFF));
+  public static final String B1_NAME = "B1" + Integer.toHexString(rand.nextInt(0xFFFFFF));
+  public static final String B2_NAME = "B2" + Integer.toHexString(rand.nextInt(0xFFFFFF));
+  public static final String B3_NAME = "B3" + Integer.toHexString(rand.nextInt(0xFFFFFF));
   public static final String C_NAME = "C" + Integer.toHexString(rand.nextInt(0xFFFFFF));
-  public static final String ROOT_NAME = "root";
+
+  public static Root createRoot(A a, C c, B... bs) {
+    Root result = new Root();
+    result.setName(ROOT_NAME);
+    if (a != null) {
+      result.setA(a);
+    }
+    if (c != null) {
+      result.setC(c);
+    }
+    for (B b : bs) {
+      result.addB(b);
+    }
+    return result;
+  }
+
+  public static A createA(String name) {
+    A result = new A();
+    result.setName(name);
+    return result;
+  }
+
+  public static B createB(String name) {
+    B result = new B();
+    result.setName(name);
+    return result;
+  }
+
+  public static B createB(String name, String otherValue) {
+    B result = new B();
+    result.setName(name);
+    result.setOtherValue(otherValue);
+    return result;
+  }
+
+  public static C createC(String name) {
+    C result = new C();
+    result.setName(name);
+    return result;
+  }
 
   public static final Function<DumpNode, String> NAME_EXTRACTOR = dp -> {
-//    List<String> result = new ArrayList<>();
     for (DumpToken dumpToken : dp.getDumpTokenList()) {
       if (dumpToken.getName().equals("Name")) {
         return dumpToken.asDumpValueToken().getValue().toString();
       }
     }
-//    return result;
     return null;
   };
 
+  public static AbstractListAssert<?, List<? extends Tuple>, Tuple, ObjectAssert<Tuple>> assertThatMapOf(Map<String, DumpNode> map) {
+    return Assertions.assertThat(map).extractingFromEntries(Map.Entry::getKey, e -> NAME_EXTRACTOR.apply(e.getValue()));
+  }
+
+  public static AbstractListAssert<?, List<?>, Object, ObjectAssert<Object>> assertThatMapOf(Map<String, List<DumpNode>> map, String key) {
+    return Assertions.assertThat(map.get(key)).flatExtracting(NAME_EXTRACTOR);
+//    return Assertions.assertThat(map).extractingFromEntries(Map.Entry::getKey, e -> {
+//      List<Object> result = new ArrayList<>();
+//      e.getValue().stream().map(NAME_EXTRACTOR).forEach(result::add);
+//      return tuple(result.toArray());
+//    });
+  }
+
   public static List<DumpNode> dumpModel(Object target) {
     return dumpModel(target, db -> {});
   }
@@ -71,74 +119,85 @@ public class TestUtils {
     return new DumpNode();
   }
 
-  public static List<DumpNode> normalChildren(DumpNode node) {
-    List<DumpNode> result = new ArrayList<>();
+  public static Map<String, DumpNode> normalChildren(DumpNode node) {
+    Map<String, DumpNode> result = new HashMap<>();
     for (DumpChildNode dumpChildNode : node.getDumpChildNodeList()) {
       if (!dumpChildNode.isList()) {
         // then it is a DumpNormalChildNode
-        result.add(((DumpNormalChildNode) dumpChildNode).getDumpNode());
+        result.put(dumpChildNode.label(), ((DumpNormalChildNode) dumpChildNode).getDumpNode());
       }
     }
     return result;
   }
 
-  public static List<DumpNode> listChildren(DumpNode node) {
-    List<DumpNode> result = new ArrayList<>();
+  public static Map<String, List<DumpNode>> listChildren(DumpNode node) {
+    Map<String, List<DumpNode>> result = new HashMap<>();
     for (DumpChildNode dumpChildNode : node.getDumpChildNodeList()) {
       if (dumpChildNode.isList()) {
         // then it is a DumpListChildNode
-        ((DumpListChildNode) dumpChildNode).getInnerDumpNodeList().forEach(inner -> result.add(inner.getDumpNode()));
+        ((DumpListChildNode) dumpChildNode).getInnerDumpNodeList().forEach(inner ->
+            result.computeIfAbsent(dumpChildNode.label(), key -> new ArrayList<>()).add(inner.getDumpNode()));
       }
     }
     return result;
   }
 
-  public static List<DumpNode> normalRelationChildren(DumpNode node) {
-    List<DumpNode> result = new ArrayList<>();
+  public static Map<String, DumpNode> normalRelationChildren(DumpNode node) {
+    Map<String, DumpNode> result = new HashMap<>();
     for (DumpRelation dumpRelation : node.getDumpRelationList()) {
       if (!dumpRelation.isList()) {
         // then it is a DumpNormalRelation
-        result.add(((DumpNormalRelation) dumpRelation).getDumpNode());
+        result.put(dumpRelation.label(), ((DumpNormalRelation) dumpRelation).getDumpNode());
       }
     }
     return result;
   }
 
-  public static List<DumpNode> listRelationChildren(DumpNode node) {
-    List<DumpNode> result = new ArrayList<>();
+  public static Map<String, List<DumpNode>> listRelationChildren(DumpNode node) {
+    Map<String, List<DumpNode>> result = new HashMap<>();
     for (DumpRelation dumpRelation : node.getDumpRelationList()) {
       if (dumpRelation.isList()) {
         // then it is a DumpListRelation
-        ((DumpListRelation) dumpRelation).getInnerDumpNodeList().forEach(inner -> result.add(inner.getDumpNode()));
+        ((DumpListRelation) dumpRelation).getInnerDumpNodeList().forEach(
+            inner -> result.computeIfAbsent(dumpRelation.label(), key -> new ArrayList<>()).add(inner.getDumpNode()));
       }
     }
     return result;
   }
 
-  public static List<DumpNode> referenceTokens(DumpNode node) {
-    List<DumpNode> result = new ArrayList<>();
+  public static Map<String, DumpNode> referenceTokens(DumpNode node) {
+    Map<String, DumpNode> result = new HashMap<>();
     for (DumpToken dumpToken : node.getDumpTokenList()) {
       if (!dumpToken.isDumpValueToken()) {
         // then it is a DumpReferenceToken
-        result.add(((DumpReferenceToken) dumpToken).getValue());
+        result.put(dumpToken.label(), ((DumpReferenceToken) dumpToken).getValue());
+      }
+    }
+    return result;
+  }
+
+  public static Map<String, Object> valueTokens(DumpNode node) {
+    Map<String, Object> result = new HashMap<>();
+    for (DumpToken dumpToken : node.getDumpTokenList()) {
+      if (dumpToken.isDumpValueToken()) {
+        // then it is a DumpValueToken
+        DumpValueToken dumpValueToken = (DumpValueToken) dumpToken;
+        result.put(dumpValueToken.label(), dumpValueToken.getValue());
       }
     }
     return result;
   }
 
-  public static Root relationSetup() {
-    Root root = new Root();
-    root.setName(ROOT_NAME);
-    A a = new A();
-    a.setName(A_NAME);
-    root.setA(a);
-    B b = new B();
-    b.setName(B_NAME);
-    root.addB(b);
-    C c = new C();
-    c.setName(C_NAME);
-    root.setC(c);
-    return root;
+  static class ExposingDumpBuilder extends DumpBuilder {
+
+    protected ExposingDumpBuilder(Object target) {
+      super(target);
+    }
+
+    @Override
+    public DumpAst build() {
+      return super.build();
+    }
   }
 
 }