diff --git a/ragconnect.base/src/main/jastadd/Intermediate.jadd b/ragconnect.base/src/main/jastadd/Intermediate.jadd
index c3c513d18e2d8f1de1b29492621c3bffc3796ab2..69ff5d8932cd33a97e44b2fa3562b13cd52617f2 100644
--- a/ragconnect.base/src/main/jastadd/Intermediate.jadd
+++ b/ragconnect.base/src/main/jastadd/Intermediate.jadd
@@ -429,9 +429,11 @@ aspect MustacheReceiveAndSendAndHandleUri {
   eq AttributeEndpointTarget.parentTypeName() = getParentTypeDecl().getName();
   eq AttributeEndpointTarget.entityName() = getName();
 
-  eq RelationEndpointTarget.getterMethodName() = "get" + getRole().getterMethodName();
+  eq RelationEndpointTarget.getterMethodName() = forwardingNTA_Name();
   eq RelationEndpointTarget.parentTypeName() = getRole().getType().getName();
   eq RelationEndpointTarget.entityName() = getRole().getName();
+  eq RelationEndpointTarget.realGetterMethodName() = "get" + getRole().getterMethodName();
+  eq RelationEndpointTarget.realGetterMethodCall() = realGetterMethodName() + (containingEndpointDefinition().indexedSend() ? "(index)" : "()");
 
   syn String NavigableRole.getterMethodName() = getName();
   eq ListRole.getterMethodName() = getName() + "List";
@@ -492,6 +494,8 @@ aspect MustacheSendDefinition {
   syn String EndpointDefinition.forwardingNTA_Name() = getEndpointTarget().forwardingNTA_Name();
   syn String EndpointDefinition.forwardingNTA_Type() = getEndpointTarget().forwardingNTA_Type();
 
+  syn boolean EndpointDefinition.relationEndpointWithListRole() = getEndpointTarget().relationEndpointWithListRole();
+
   syn String EndpointDefinition.senderName() = getEndpointTarget().senderName();
 
   syn boolean EndpointDefinition.shouldNotResetValue() = getSend() && !getEndpointTarget().hasAttributeResetMethod();
@@ -505,13 +509,17 @@ aspect MustacheSendDefinition {
   // === attributes needed for computing above ones ===
   syn boolean EndpointTarget.needForwardingNTA() = false;
   eq TypeEndpointTarget.needForwardingNTA() = containingEndpointDefinition().getSend() && !getType().getNTA();
+  eq RelationEndpointTarget.needForwardingNTA() = containingEndpointDefinition().getSend();
 
-  syn String EndpointTarget.forwardingNTA_Name() = null;
+  syn String EndpointTarget.forwardingNTA_Name() = null;  // only needed, if needForwardingNTA evaluates to true
   eq TypeEndpointTarget.forwardingNTA_Name() = ragconnect().internalRagConnectPrefix() + getType().getName();
+  eq RelationEndpointTarget.forwardingNTA_Name() = ragconnect().internalRagConnectPrefix() + getRole().getName();
 
-  syn String EndpointTarget.forwardingNTA_Type() = null;
+  syn String EndpointTarget.forwardingNTA_Type() = null;  // only needed, if needForwardingNTA evaluates to true
   eq TypeEndpointTarget.forwardingNTA_Type() = getType().forwardingNTA_Type(
           containingEndpointDefinition().getIndexBasedListAccess());
+  eq RelationEndpointTarget.forwardingNTA_Type() = getRole().forwardingNTA_Type(
+containingEndpointDefinition().getIndexBasedListAccess());
 
   syn String TypeComponent.forwardingNTA_Type(boolean indexBasedListAccess);
   eq NormalComponent.forwardingNTA_Type(boolean indexBasedListAccess) = getTypeDecl().getName();
@@ -521,6 +529,14 @@ aspect MustacheSendDefinition {
           getTypeDecl().getName() :
           ragconnect().configJastAddList() + "<" + getTypeDecl().getName() + ">";
 
+  syn String Role.forwardingNTA_Type(boolean indexBasedListAccess) = oppositeRole().getType().getName();
+  eq ListRole.forwardingNTA_Type(boolean indexBasedListAccess) = indexBasedListAccess ?
+          oppositeRole().getType().getName() :
+          "java.util.List<" + oppositeRole().getType().getName() + ">";
+
+  syn boolean EndpointTarget.relationEndpointWithListRole() = false;
+  eq RelationEndpointTarget.relationEndpointWithListRole() = getRole().isListRole();
+
   syn String EndpointTarget.senderName() = ragconnect().internalRagConnectPrefix() + "_sender_" + entityName();
   eq ContextFreeTypeEndpointTarget.senderName() = null;
 
diff --git a/ragconnect.base/src/main/resources/sendDefinition.mustache b/ragconnect.base/src/main/resources/sendDefinition.mustache
index ba6661bb20daa90926eba165121a8e8aeb400293..1e66fdedcc7620aefe31a89fcece47dbcf9708ac 100644
--- a/ragconnect.base/src/main/resources/sendDefinition.mustache
+++ b/ragconnect.base/src/main/resources/sendDefinition.mustache
@@ -123,5 +123,20 @@ protected void {{parentTypeName}}.{{writeMethodName}}({{#IndexBasedListAccess}}i
 }
 
 {{#needForwardingNTA}}
-syn {{{forwardingNTA_Type}}} {{parentTypeName}}.{{forwardingNTA_Name}}({{#IndexBasedListAccess}}int index{{/IndexBasedListAccess}}) = {{realGetterMethodCall}}.{{touchedTerminalsMethodName}}();
+syn {{{forwardingNTA_Type}}} {{parentTypeName}}.{{forwardingNTA_Name}}({{#IndexBasedListAccess}}int index{{/IndexBasedListAccess}}) {
+{{#relationEndpointWithListRole}}
+//  for (var element : {{realGetterMethodCall}}) {
+//    element.{{touchedTerminalsMethodName}}();
+//  }
+  {{realGetterMethodCall}}.stream().forEach(element -> element.{{touchedTerminalsMethodName}}());
+  return {{realGetterMethodCall}};
+{{/relationEndpointWithListRole}}
+{{^relationEndpointWithListRole}}
+  {{{forwardingNTA_Type}}} result = {{realGetterMethodCall}};
+  if (result == null) {
+    return null;
+  }
+  return result.{{touchedTerminalsMethodName}}();
+{{/relationEndpointWithListRole}}
+}
 {{/needForwardingNTA}}
diff --git a/ragconnect.tests/src/test/01-input/relation/Test.connect b/ragconnect.tests/src/test/01-input/relation/Test.connect
index b122934e2a7ea82ac6923a1426cb96da90fe9aaf..61e81d25e3a3bcb898a59112340402c7f21ecccc 100644
--- a/ragconnect.tests/src/test/01-input/relation/Test.connect
+++ b/ragconnect.tests/src/test/01-input/relation/Test.connect
@@ -1,18 +1,18 @@
 send SenderRoot.MyA;
 send SenderRoot.OptionalA;
-send SenderRoot.ListA;
+send SenderRoot.ManyA;
 
 send SenderRoot.BiMyA;
 send SenderRoot.BiOptionalA;
-send SenderRoot.BiListA;
+send SenderRoot.BiManyA;
 
 send SenderRoot.MyB using ConcatValues;
 send SenderRoot.OptionalB using ConcatValues;
-send SenderRoot.ListB using ConcatValueList;
+send SenderRoot.ManyB using ConcatValueList;
 
 send SenderRoot.BiMyB using ConcatValues;
 send SenderRoot.BiOptionalB using ConcatValues;
-send SenderRoot.BiListB using ConcatValueList;
+send SenderRoot.BiManyB using ConcatValueList;
 
 ConcatValues maps B b to String {:
   return b.getValue() + "+" + b.getInner().getInnerValue();
@@ -28,16 +28,16 @@ ConcatValueList maps java.util.List<B> list to String {:
 
 receive ReceiverRoot.FromMyA;
 receive ReceiverRoot.FromOptionalA;
-receive ReceiverRoot.FromListA;
+receive ReceiverRoot.FromManyA;
 
 receive ReceiverRoot.FromBiMyA;
 receive ReceiverRoot.FromBiOptionalA;
-receive ReceiverRoot.FromBiListA;
+receive ReceiverRoot.FromBiManyA;
 
 receive ReceiverRoot.FromMyB;
 receive ReceiverRoot.FromOptionalB;
-receive ReceiverRoot.FromListB;
+receive ReceiverRoot.FromManyB;
 
 receive ReceiverRoot.FromBiMyB;
 receive ReceiverRoot.FromBiOptionalB;
-receive ReceiverRoot.FromBiListB;
+receive ReceiverRoot.FromBiManyB;
diff --git a/ragconnect.tests/src/test/01-input/relation/Test.relast b/ragconnect.tests/src/test/01-input/relation/Test.relast
index d0cc7790d80eeeb27de65b68a52cd615bfc5761f..7effc867e0f5f45f444ac87607be607f1a23e31a 100644
--- a/ragconnect.tests/src/test/01-input/relation/Test.relast
+++ b/ragconnect.tests/src/test/01-input/relation/Test.relast
@@ -2,25 +2,25 @@ Root ::= SenderRoot* ReceiverRoot;
 SenderRoot ::= A* B* ;
 rel SenderRoot.MyA -> A;
 rel SenderRoot.OptionalA? -> A;
-rel SenderRoot.ListA* -> A;
+rel SenderRoot.ManyA* -> A;
 
 rel SenderRoot.BiMyA <-> A.ToMyA?;
 rel SenderRoot.BiOptionalA? <-> A.ToOptionalA?;
-rel SenderRoot.BiListA* <-> A.ToListA?;
+rel SenderRoot.BiManyA* <-> A.ToManyA?;
 
 rel SenderRoot.MyB -> B;
 rel SenderRoot.OptionalB? -> B;
-rel SenderRoot.ListB* -> B;
+rel SenderRoot.ManyB* -> B;
 
 rel SenderRoot.BiMyB <-> B.ToMyB?;
 rel SenderRoot.BiOptionalB? <-> B.ToOptionalB?;
-rel SenderRoot.BiListB* <-> B.ToListB?;
+rel SenderRoot.BiManyB* <-> B.ToManyB?;
 
 ReceiverRoot ::=
-FromMyA:A   FromOptionalA:A   FromListA:A*
-FromBiMyA:A FromBiOptionalA:A FromBiListA:A*
-<FromMyB:String>   <FromOptionalB:String>   <FromListB:String>
-<FromBiMyB:String> <FromBiOptionalB:String> <FromBiListB:String>
+FromMyA:A   FromOptionalA:A   FromManyA:A*
+FromBiMyA:A FromBiOptionalA:A FromBiManyA:A*
+<FromMyB:String>   <FromOptionalB:String>   <FromManyB:String>
+<FromBiMyB:String> <FromBiOptionalB:String> <FromBiManyB:String>
 ;
 
 A ::= <Value> Inner ;
diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/RelationTest.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/RelationTest.java
index b8349f30943bd8d9d02e71df185367b7aa521d30..c2affb34586f144a6462f31021c1ba1d2b44d6c1 100644
--- a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/RelationTest.java
+++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/RelationTest.java
@@ -1,19 +1,17 @@
 package org.jastadd.ragconnect.tests;
 
 import org.assertj.core.groups.Tuple;
-import relationInc.ast.*;
 import org.junit.jupiter.api.Tag;
+import relationInc.ast.*;
 
 import java.io.IOException;
 import java.util.List;
 import java.util.concurrent.Callable;
 import java.util.concurrent.TimeUnit;
-import java.util.function.Predicate;
 
 import static java.util.function.Predicate.isEqual;
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.groups.Tuple.tuple;
-import static org.hamcrest.Matchers.hasProperty;
 import static org.jastadd.ragconnect.tests.TestUtils.*;
 import static org.junit.jupiter.api.Assertions.*;
 
@@ -29,16 +27,16 @@ public class RelationTest extends AbstractMqttTest {
   private static final String TOPIC_WILDCARD = "rel/#";
   private static final String TOPIC_MY_A = "rel/my_a";
   private static final String TOPIC_OPTIONAL_A = "rel/optional_a";
-  private static final String TOPIC_LIST_A = "rel/list_a";
+  private static final String TOPIC_MANY_A = "rel/many_a";
   private static final String TOPIC_BI_MY_A = "rel/bi_my_a";
   private static final String TOPIC_BI_OPTIONAL_A = "rel/bi_optional_a";
-  private static final String TOPIC_BI_LIST_A = "rel/bi_list_a";
+  private static final String TOPIC_BI_MANY_A = "rel/bi_many_a";
   private static final String TOPIC_MY_B = "rel/my_b";
   private static final String TOPIC_OPTIONAL_B = "rel/optional_b";
-  private static final String TOPIC_LIST_B = "rel/list_b";
+  private static final String TOPIC_MANY_B = "rel/many_b";
   private static final String TOPIC_BI_MY_B = "rel/bi_my_b";
   private static final String TOPIC_BI_OPTIONAL_B = "rel/bi_optional_b";
-  private static final String TOPIC_BI_LIST_B = "rel/bi_list_b";
+  private static final String TOPIC_BI_MANY_B = "rel/bi_many_b";
 
   private MqttHandler handler;
   private ReceiverData data;
@@ -52,21 +50,13 @@ public class RelationTest extends AbstractMqttTest {
   protected void createModel() {
     model = new Root();
 //    model.trace().setReceiver(TestUtils::logEvent);
-    senderUni = new SenderRoot();
-    A a1 = createA("a1");
-    senderUni.addA(a1);
-    senderUni.setMyA(a1);
-    senderUni.setBiMyA(a1);
-    senderUni.addA(createA("a2"));
-    senderUni.addA(createA("a3"));
-
-    senderBi = new SenderRoot();
-    B b1 = createB("b1");
-    senderUni.addB(b1);
-    senderUni.setMyB(b1);
-    senderUni.setBiMyB(b1);
-    senderUni.addB(createB("b2"));
-    senderUni.addB(createB("b3"));
+    senderUni = createSenderRoot("uni");
+    senderUni.setMyA(uniA(1));
+    senderUni.setMyB(uniB(1));
+
+    senderBi = createSenderRoot("bi");
+    senderBi.setBiMyA(biA(1));
+    senderBi.setBiMyB(biB(1));
 
     receiverRoot = new ReceiverRoot();
     model.addSenderRoot(senderUni);
@@ -74,14 +64,52 @@ public class RelationTest extends AbstractMqttTest {
     model.setReceiverRoot(receiverRoot);
   }
 
+  private SenderRoot createSenderRoot(String name) {
+    SenderRoot result = new SenderRoot();
+    A dummyA = createA(name + "-dummyA");
+    result.addA(dummyA);
+    result.addA(createA(name + "-a1"));
+    result.addA(createA(name + "-a2"));
+    result.addA(createA(name + "-a3"));
+
+    B dummyB = createB(name + "-dummyB");
+    result.addB(dummyB);
+    result.addB(createB(name + "-b1"));
+    result.addB(createB(name + "-b2"));
+    result.addB(createB(name + "-b3"));
+
+    result.setMyA(dummyA);
+    result.setBiMyA(dummyA);
+    result.setMyB(dummyB);
+    result.setBiMyB(dummyB);
+
+    return result;
+  }
+
   private A createA(String value) {
     return new A().setValue(value)
-            .setInner(new Inner().setInnerValue("inner" + value));
+            .setInner(new Inner().setInnerValue("inner-" + value));
   }
 
   private B createB(String value) {
     return new B().setValue(value)
-            .setInner(new Inner().setInnerValue("inner" + value));
+            .setInner(new Inner().setInnerValue("inner-" + value));
+  }
+
+  private A uniA(int index) {
+    return senderUni.getA(index);
+  }
+
+  private B uniB(int index) {
+    return senderUni.getB(index);
+  }
+
+  private A biA(int index) {
+    return senderBi.getA(index);
+  }
+
+  private B biB(int index) {
+    return senderBi.getB(index);
   }
 
   @Override
@@ -96,33 +124,33 @@ public class RelationTest extends AbstractMqttTest {
     // connect receive
     assertTrue(receiverRoot.connectFromMyA(mqttUri(TOPIC_MY_A)));
     assertTrue(receiverRoot.connectFromOptionalA(mqttUri(TOPIC_OPTIONAL_A)));
-    assertTrue(receiverRoot.connectFromListAList(mqttUri(TOPIC_LIST_A)));
+    assertTrue(receiverRoot.connectFromManyAList(mqttUri(TOPIC_MANY_A)));
     assertTrue(receiverRoot.connectFromBiMyA(mqttUri(TOPIC_BI_MY_A)));
     assertTrue(receiverRoot.connectFromBiOptionalA(mqttUri(TOPIC_BI_OPTIONAL_A)));
-    assertTrue(receiverRoot.connectFromBiListAList(mqttUri(TOPIC_BI_LIST_A)));
+    assertTrue(receiverRoot.connectFromBiManyAList(mqttUri(TOPIC_BI_MANY_A)));
     assertTrue(receiverRoot.connectFromMyB(mqttUri(TOPIC_MY_B)));
     assertTrue(receiverRoot.connectFromOptionalB(mqttUri(TOPIC_OPTIONAL_B)));
-    assertTrue(receiverRoot.connectFromListB(mqttUri(TOPIC_LIST_B)));
+    assertTrue(receiverRoot.connectFromManyB(mqttUri(TOPIC_MANY_B)));
     assertTrue(receiverRoot.connectFromBiMyB(mqttUri(TOPIC_BI_MY_B)));
     assertTrue(receiverRoot.connectFromBiOptionalB(mqttUri(TOPIC_BI_OPTIONAL_B)));
-    assertTrue(receiverRoot.connectFromBiListB(mqttUri(TOPIC_BI_LIST_B)));
+    assertTrue(receiverRoot.connectFromBiManyB(mqttUri(TOPIC_BI_MANY_B)));
 
     // connect send, and wait to receive (if writeCurrentValue is set)
     assertTrue(senderUni.connectMyA(mqttUri(TOPIC_MY_A), isWriteCurrentValue()));
     assertTrue(senderUni.connectOptionalA(mqttUri(TOPIC_OPTIONAL_A), isWriteCurrentValue()));
-    assertTrue(senderUni.connectListA(mqttUri(TOPIC_LIST_A), isWriteCurrentValue()));
+    assertTrue(senderUni.connectManyA(mqttUri(TOPIC_MANY_A), isWriteCurrentValue()));
 
     assertTrue(senderBi.connectBiMyA(mqttUri(TOPIC_BI_MY_A), isWriteCurrentValue()));
     assertTrue(senderBi.connectBiOptionalA(mqttUri(TOPIC_BI_OPTIONAL_A), isWriteCurrentValue()));
-    assertTrue(senderBi.connectBiListA(mqttUri(TOPIC_BI_LIST_A), isWriteCurrentValue()));
+    assertTrue(senderBi.connectBiManyA(mqttUri(TOPIC_BI_MANY_A), isWriteCurrentValue()));
 
     assertTrue(senderUni.connectMyB(mqttUri(TOPIC_MY_B), isWriteCurrentValue()));
     assertTrue(senderUni.connectOptionalB(mqttUri(TOPIC_OPTIONAL_B), isWriteCurrentValue()));
-    assertTrue(senderUni.connectListB(mqttUri(TOPIC_LIST_B), isWriteCurrentValue()));
+    assertTrue(senderUni.connectManyB(mqttUri(TOPIC_MANY_B), isWriteCurrentValue()));
 
     assertTrue(senderBi.connectBiMyB(mqttUri(TOPIC_BI_MY_B), isWriteCurrentValue()));
     assertTrue(senderBi.connectBiOptionalB(mqttUri(TOPIC_BI_OPTIONAL_B), isWriteCurrentValue()));
-    assertTrue(senderBi.connectBiListB(mqttUri(TOPIC_BI_LIST_B), isWriteCurrentValue()));
+    assertTrue(senderBi.connectBiManyB(mqttUri(TOPIC_BI_MANY_B), isWriteCurrentValue()));
 
 //    waitForNonNull(receiverRoot::getFromMyA);
 //    waitForNonNull(receiverRoot::getFromBiMyA);
@@ -130,24 +158,146 @@ public class RelationTest extends AbstractMqttTest {
 //    waitForNonNull(receiverRoot::getFromBiMyB);
   }
 
-  private <T> void waitForValue(T expectedValue, Callable<T> callable) {
-    if (isWriteCurrentValue()) {
-      awaitMqtt().until(callable, isEqual(expectedValue));
-    }
-  }
-
-  private <T> void waitForNonNull(Callable<T> callable) {
-    if (isWriteCurrentValue()) {
-      awaitMqtt().until(callable, Predicate.not(isEqual(null)));
-    }
-  }
+//  private <T> void waitForValue(T expectedValue, Callable<T> callable) {
+//    if (isWriteCurrentValue()) {
+//      awaitMqtt().until(callable, isEqual(expectedValue));
+//    }
+//  }
+//
+//  private <T> void waitForNonNull(Callable<T> callable) {
+//    if (isWriteCurrentValue()) {
+//      awaitMqtt().until(callable, Predicate.not(isEqual(null)));
+//    }
+//  }
 
   @Override
   protected void communicateSendInitialValue() throws IOException, InterruptedException {
-    // TODO implement test
     // TODO also check disconnect
-    check(6, "a1", null, tuple(), null, null, tuple(),
-            "b1", null, tuple(), null, null, tuple());
+    // myA -> a1, myB -> b1
+    // biMyA -> a1, biMyB -> b1
+    check(8, "uni-a1", null, tuple(), "bi-a1", null, tuple(),
+            "uni-b1", null, tuple(), "bi-b1", null, tuple());
+
+    // --- testing unmapped unidirectional normal role --- //
+
+    uniA(1).setValue("test-1");
+    check(9, "test-1:inner-uni-a1", null, tuple(), "bi-a1", null, tuple(),
+            "uni-b1", null, tuple(), "bi-b1", null, tuple());
+
+    senderUni.setMyA(uniA(2));
+    check(10, "uni-a2", null, tuple(), "bi-a1", null, tuple(),
+            "uni-b1", null, tuple(), "bi-b1", null, tuple());
+
+    // changing something that was previously the relation target must not trigger a message
+    uniA(1).setValue("test-2-ignored");
+    check(10, "uni-a2", null, tuple(), "bi-a1", null, tuple(),
+            "uni-b1", null, tuple(), "bi-b1", null, tuple(), true);
+
+    uniA(2).setValue("test-3");
+    check(11, "test-3:inner-uni-a2", null, tuple(), "bi-a1", null, tuple(),
+            "uni-b1", null, tuple(), "bi-b1", null, tuple());
+
+    uniA(2).getInner().setInnerValue("test-4");
+    check(12, "test-3:test-4", null, tuple(), "bi-a1", null, tuple(),
+            "uni-b1", null, tuple(), "bi-b1", null, tuple());
+
+    // setting a new relation target resulting in the same serialization must not trigger a message
+    uniA(1).setValue("test-3");
+    uniA(1).getInner().setInnerValue("test-4");
+    senderUni.setMyA(uniA(1));
+    check(12, "test-3:test-4", null, tuple(), "bi-a1", null, tuple(),
+            "uni-b1", null, tuple(), "bi-b1", null, tuple(), true);
+
+    // --- testing unmapped unidirectional optional role --- //
+
+    // reset a2
+    uniA(2).setValue("uni-a2");
+    uniA(2).getInner().setInnerValue("inner-uni-a2");
+
+    senderUni.setOptionalA(uniA(2));
+    check(13, "test-3:test-4", "uni-a2", tuple(), "bi-a1", null, tuple(),
+            "uni-b1", null, tuple(), "bi-b1", null, tuple());
+
+    uniA(2).setValue("test-5");
+    check(14, "test-3:test-4", "test-5:inner-uni-a2", tuple(), "bi-a1", null, tuple(),
+            "uni-b1", null, tuple(), "bi-b1", null, tuple());
+
+    senderUni.setOptionalA(uniA(1));
+    check(15, "test-3:test-4", "test-3:test-4", tuple(), "bi-a1", null, tuple(),
+            "uni-b1", null, tuple(), "bi-b1", null, tuple());
+
+    // change a nonterminal target of two relations must trigger two messages
+    uniA(1).getInner().setInnerValue("test-6");
+    check(17, "test-3:test-6", "test-3:test-6", tuple(), "bi-a1", null, tuple(),
+            "uni-b1", null, tuple(), "bi-b1", null, tuple());
+
+    // setting an optional relation to null is allowed, but must not trigger a message
+    senderUni.setOptionalA(null);
+    check(17, "test-3:test-6", "test-3:test-6", tuple(), "bi-a1", null, tuple(),
+            "uni-b1", null, tuple(), "bi-b1", null, tuple(), true);
+
+    // setting the previous nonterminal as relation target again won't trigger a message
+    senderUni.setOptionalA(uniA(1));
+    check(17, "test-3:test-6", "test-3:test-6", tuple(), "bi-a1", null, tuple(),
+            "uni-b1", null, tuple(), "bi-b1", null, tuple(), true);
+
+    // --- testing unmapped unidirectional list role --- //
+
+    senderUni.addManyA(uniA(3));
+    check(18, "test-3:test-6", "test-3:test-6", tuple("uni-a3"), "bi-a1", null, tuple(),
+            "uni-b1", null, tuple(), "bi-b1", null, tuple());
+
+    uniA(3).setValue("test-7");
+    check(19, "test-3:test-6", "test-3:test-6", tuple("test-7:inner-uni-a3"), "bi-a1", null, tuple(),
+            "uni-b1", null, tuple(), "bi-b1", null, tuple());
+
+    senderUni.addManyA(uniA(2));
+    check(20, "test-3:test-6", "test-3:test-6", tuple("test-7:inner-uni-a3", "test-5:inner-uni-a2"), "bi-a1", null, tuple(),
+            "uni-b1", null, tuple(), "bi-b1", null, tuple());
+
+    senderUni.addManyA(uniA(1));
+    check(21, "test-3:test-6", "test-3:test-6", tuple("test-7:inner-uni-a3", "test-5:inner-uni-a2", "test-3:test-6"), "bi-a1", null, tuple(),
+            "uni-b1", null, tuple(), "bi-b1", null, tuple());
+
+    uniA(2).getInner().setInnerValue("test-8");
+    check(22, "test-3:test-6", "test-3:test-6", tuple("test-7:inner-uni-a3", "test-5:test-8", "test-3:test-6"), "bi-a1", null, tuple(),
+            "uni-b1", null, tuple(), "bi-b1", null, tuple());
+
+    senderUni.removeManyA(uniA(2));
+    check(23, "test-3:test-6", "test-3:test-6", tuple("test-7:inner-uni-a3", "test-3:test-6"), "bi-a1", null, tuple(),
+            "uni-b1", null, tuple(), "bi-b1", null, tuple());
+
+    // disconnect my-a, optional-a, many-a - resetting afterwards must not trigger a message
+    senderUni.disconnectMyA(mqttUri(TOPIC_MY_A));
+    senderUni.disconnectOptionalA(mqttUri(TOPIC_OPTIONAL_A));
+    senderUni.disconnectManyA(mqttUri(TOPIC_MANY_A));
+    uniA(1).setValue("a1");
+    uniA(1).getInner().setInnerValue("inner-a1");
+    uniA(2).setValue("a2");
+    uniA(2).getInner().setInnerValue("inner-a2");
+    uniA(3).setValue("a3");
+    uniA(3).getInner().setInnerValue("inner-a3");
+    check(23, "test-3:test-6", "test-3:test-6", tuple("test-7:inner-uni-a3", "test-3:test-6"), "bi-a1", null, tuple(),
+            "uni-b1", null, tuple(), "bi-b1", null, tuple(), true);
+
+    // --- todo testing unmapped bidirectional normal role --- //
+
+    // --- todo testing unmapped bidirectional optional role --- //
+
+    // --- todo testing unmapped bidirectional list role --- //
+
+    // --- todo testing transformed unidirectional normal role --- //
+
+    // --- todo testing transformed unidirectional optional role --- //
+
+    // --- todo testing transformed unidirectional list role --- //
+
+    // --- todo testing transformed bidirectional normal role --- //
+
+    // --- todo testing transformed bidirectional optional role --- //
+
+    // --- todo testing transformed bidirectional list role --- //
+
   }
 
   @Override
@@ -158,10 +308,22 @@ public class RelationTest extends AbstractMqttTest {
   }
 
   private void check(int numberOfValues,
-                     String myA, String optionalA, Tuple listA,
-                     String biMyA, String biOptionalA, Tuple biListA,
-                     String myB, String optionalB, Tuple listB,
-                     String biMyB, String biOptionalB, Tuple biListB) {
+                     String myA, String optionalA, Tuple manyA,
+                     String biMyA, String biOptionalA, Tuple biManyA,
+                     String myB, String optionalB, Tuple manyB,
+                     String biMyB, String biOptionalB, Tuple biManyB) throws InterruptedException {
+    check(numberOfValues, myA, optionalA, manyA, biMyA, biOptionalA, biManyA,
+            myB, optionalB, manyB, biMyB, biOptionalB, biManyB, false);
+  }
+
+  private void check(int numberOfValues,
+                     String myA, String optionalA, Tuple manyA,
+                     String biMyA, String biOptionalA, Tuple biManyA,
+                     String myB, String optionalB, Tuple manyB,
+                     String biMyB, String biOptionalB, Tuple biManyB, boolean wait) throws InterruptedException {
+    if (wait) {
+      waitForMqtt();
+    }
     awaitEquals(numberOfValues, () -> data.numberOfValues, "numberOfValues");
 
 //    awaitEquals(Objects.requireNonNullElse(basic, ""),
@@ -170,20 +332,20 @@ public class RelationTest extends AbstractMqttTest {
     // A values
     assertNullOrA(myA, receiverRoot.getFromMyA(), "myA");
     assertNullOrA(optionalA, receiverRoot.getFromOptionalA(), "optionalA");
-    assertListEqualsForA(listA.toList(), receiverRoot.getFromListAList(), "listA");
+    assertListEqualsForA(manyA.toList(), receiverRoot.getFromManyAList(), "manyA");
 
     assertNullOrA(biMyA, receiverRoot.getFromBiMyA(), "biMyA");
     assertNullOrA(biOptionalA, receiverRoot.getFromBiOptionalA(), "biOptionalA");
-    assertListEqualsForA(biListA.toList(), receiverRoot.getFromBiListAList(), "biListA");
+    assertListEqualsForA(biManyA.toList(), receiverRoot.getFromBiManyAList(), "biManyA");
 
     // B values
     assertNullOrB(myB, receiverRoot.getFromMyB(), "myB");
     assertNullOrB(optionalB, receiverRoot.getFromOptionalB(), "optionalB");
-    assertListEqualsForB(listB.toList(), receiverRoot.getFromListB(), "listB");
+    assertListEqualsForB(manyB.toList(), receiverRoot.getFromManyB(), "manyB");
 
     assertNullOrB(biMyB, receiverRoot.getFromBiMyB(), "biMyB");
     assertNullOrB(biOptionalB, receiverRoot.getFromBiOptionalB(), "biOptionalB");
-    assertListEqualsForB(biListB.toList(), receiverRoot.getFromBiListB(), "biListB");
+    assertListEqualsForB(biManyB.toList(), receiverRoot.getFromBiManyB(), "biManyB");
   }
 
   private <T> void awaitEquals(T expected, Callable<T> actual, String alias) {
@@ -195,7 +357,15 @@ public class RelationTest extends AbstractMqttTest {
       assertNull(actual, alias);
       return;
     }
-    String expectedInner = "inner" + expectedValue;
+    final String expectedInner;
+    if (expectedValue.contains(":")) {
+      String[] tokens = expectedValue.split(":");
+      assertEquals(2, tokens.length);
+      expectedValue = tokens[0];
+      expectedInner = tokens[1];
+    } else {
+      expectedInner = "inner-" + expectedValue;
+    }
     assertThat(actual.getValue()).describedAs(alias + ".Value").isEqualTo(expectedValue);
     assertThat(actual.getInner()).describedAs(alias + ".inner != null").isNotNull();
     assertThat(actual.getInner().getInnerValue()).describedAs(alias + ".inner.Value").isEqualTo(expectedInner);
@@ -214,7 +384,7 @@ public class RelationTest extends AbstractMqttTest {
     if (expected == null) {
       expectedValue = "";
     } else {
-      expectedValue = expected + "+inner" + expected;
+      expectedValue = expected + "+inner-" + expected;
     }
     assertEquals(expectedValue, actual, alias);
   }