diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 64a4f56c03ca376a41c8375bc46d4041baa96e02..4989fda9ed75ff08ae1e20e92038258397b4f527 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -21,7 +21,7 @@ test:
   image: openjdk:8
   stage: test
   script:
-    - ./gradlew test
+    - ./gradlew allTests
   artifacts:
     reports:
       junit: ros2rag.tests/build/test-results/test/TEST-*.xml
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000000000000000000000000000000000000..d27fb4030353056cd7745ff9f57d6a913389ccee
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,22 @@
+FROM ubuntu:bionic
+
+ENV DEBIAN_FRONTEND noninteractive
+ENV HOME /home/user
+RUN useradd --create-home --home-dir $HOME user \
+    && chmod -R u+rwx $HOME \
+    && chown -R user:user $HOME
+
+RUN apt-get update -y \
+    && apt-get upgrade -y \
+    && apt-get install -y --no-install-recommends openjdk-8-jdk \
+    && apt-get clean
+
+COPY --chown=user:user . /ros2rag/
+
+USER user
+WORKDIR /ros2rag
+
+RUN ./gradlew --no-daemon installDist
+
+ENTRYPOINT /bin/bash -c "./ros2rag.starter/build/install/ros2rag.starter/bin/ros2rag.starter ./ros2rag.starter/src/main/resources/config.json"
+
diff --git a/ros2rag.base/src/main/jastadd/backend/Generation.jadd b/ros2rag.base/src/main/jastadd/backend/Generation.jadd
index 81e2dabe41761ef3e1b3058adb2809be90f6ac1d..abce6a07b80fb96313de4760511c5339e9da92a0 100644
--- a/ros2rag.base/src/main/jastadd/backend/Generation.jadd
+++ b/ros2rag.base/src/main/jastadd/backend/Generation.jadd
@@ -141,16 +141,23 @@ aspect AspectGeneration {
     sb.append(ind(indent + 1)).append(preemptiveReturnStatement()).append("\n");
     sb.append(ind(indent)).append("}\n");
     if (!getAlwaysApply()) {
-      sb.append(ind(indent)).append("if (").append(preemptiveExpectedValue());
-      if (getToken().isPrimitiveType()) {
-        sb.append(" == ").append(inputVariableName);
-      } else if (effectiveMappings().get(effectiveMappings().size() - 1).isDefaultMappingDefinition()) {
-        sb.append(" != null && ").append(preemptiveExpectedValue()).append(".equals(")
-          .append(inputVariableName).append(")");
+      MappingDefinition lastMapping = effectiveMappings().get(effectiveMappings().size() - 1);
+      sb.append(ind(indent)).append("if (");
+      if (lastMapping.getToType().isArray()) {
+        sb.append("java.util.Arrays.equals(").append(preemptiveExpectedValue())
+          .append(", ").append(inputVariableName).append(")");
       } else {
-        sb.append(" != null ? ").append(preemptiveExpectedValue()).append(".equals(")
-          .append(inputVariableName).append(")").append(" : ")
-          .append(inputVariableName).append(" == null");
+        sb.append(preemptiveExpectedValue());
+        if (getToken().isPrimitiveType() && lastMapping.getToType().isPrimitiveType()) {
+          sb.append(" == ").append(inputVariableName);
+        } else if (lastMapping.isDefaultMappingDefinition()) {
+          sb.append(" != null && ").append(preemptiveExpectedValue()).append(".equals(")
+            .append(inputVariableName).append(")");
+        } else {
+          sb.append(" != null ? ").append(preemptiveExpectedValue()).append(".equals(")
+            .append(inputVariableName).append(")").append(" : ")
+            .append(inputVariableName).append(" == null");
+        }
       }
       sb.append(") { ").append(preemptiveReturnStatement()).append(" }\n");
     }
diff --git a/ros2rag.base/src/main/jastadd/backend/Mappings.jrag b/ros2rag.base/src/main/jastadd/backend/Mappings.jrag
index 7add42fa7e3d3f7c71cb35b5a34c8c5d05b83628..fd40385d9e8879dd188362f6a0acd802652f511e 100644
--- a/ros2rag.base/src/main/jastadd/backend/Mappings.jrag
+++ b/ros2rag.base/src/main/jastadd/backend/Mappings.jrag
@@ -151,6 +151,12 @@ aspect Mappings {
       default: return false;
     }
   }
+  syn boolean MappingDefinitionType.isPrimitiveType() = false;
+  eq JavaMappingDefinitionType.isPrimitiveType() = getType().isPrimitiveType();
+
+  // --- isArray ---
+  syn boolean MappingDefinitionType.isArray() = false;
+  eq JavaArrayMappingDefinitionType.isArray() = true;
 
   // --- suitableDefaultMapping ---
   syn DefaultMappingDefinition UpdateDefinition.suitableDefaultMapping();
diff --git a/ros2rag.base/src/main/resources/MqttUpdater.jadd b/ros2rag.base/src/main/resources/MqttUpdater.jadd
index 394735cc78595ad0ebe15b5ab1fa6e73ef435b13..df1dc10054722b249c10637781bc896eebdaf37f 100644
--- a/ros2rag.base/src/main/resources/MqttUpdater.jadd
+++ b/ros2rag.base/src/main/resources/MqttUpdater.jadd
@@ -231,8 +231,16 @@ public class MqttUpdater {
   }
 
   public void publish(String topic, byte[] bytes) {
+    publish(topic, bytes, false);
+  }
+
+  public void publish(String topic, byte[] bytes, boolean retain) {
+    publish(topic, bytes, this.qos, retain);
+  }
+
+  public void publish(String topic, byte[] bytes, org.fusesource.mqtt.client.QoS qos, boolean retain) {
     connection.getDispatchQueue().execute(() -> {
-      connection.publish(topic, bytes, qos, false, new org.fusesource.mqtt.client.Callback<Void>() {
+      connection.publish(topic, bytes, qos, retain, new org.fusesource.mqtt.client.Callback<Void>() {
         @Override
         public void onSuccess(Void value) {
           logger.debug("Published some bytes to {}", topic);
diff --git a/ros2rag.receiverstub/src/main/java/de/tudresden/inf/st/ros2rag/receiverstub/Main.java b/ros2rag.receiverstub/src/main/java/de/tudresden/inf/st/ros2rag/receiverstub/Main.java
index 8c80b172671bc2b6eed937cca1cad99cf61d920e..f954120848dad5a83d498f91c6c33bdceb32969f 100644
--- a/ros2rag.receiverstub/src/main/java/de/tudresden/inf/st/ros2rag/receiverstub/Main.java
+++ b/ros2rag.receiverstub/src/main/java/de/tudresden/inf/st/ros2rag/receiverstub/Main.java
@@ -7,6 +7,7 @@ import config.Robotconfig.RobotConfig;
 import de.tudresden.inf.st.ros2rag.starter.ast.MqttUpdater;
 import de.tudresden.inf.st.ros2rag.starter.data.DataConfiguration;
 import de.tudresden.inf.st.ros2rag.starter.data.DataJoint;
+import org.apache.logging.log4j.Level;
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
 import panda.Linkstate.PandaLinkState;
@@ -19,6 +20,7 @@ import java.util.concurrent.TimeUnit;
 public class Main {
 
   private static final Logger logger = LogManager.getLogger(Main.class);
+  private String topicPattern;
 
   public static void main(String[] args) throws Exception {
     Main main = new Main();
@@ -32,6 +34,7 @@ public class Main {
   private void run(DataConfiguration config) throws IOException, InterruptedException {
     final CountDownLatch finish = new CountDownLatch(1);
 
+    int topicMaxLength = 0;
     MqttUpdater receiver = new MqttUpdater("receiver stub");
     receiver.setHost(config.mqttHost);
     receiver.waitUntilReady(2, TimeUnit.SECONDS);
@@ -39,7 +42,9 @@ public class Main {
     receiver.newConnection(config.dataConfigTopic, this::printDataConfig);
     for (DataJoint joint : config.joints) {
       receiver.newConnection(joint.topic, this::printPandaLinkState);
+      topicMaxLength = Math.max(topicMaxLength, joint.topic.length());
     }
+    this.topicPattern = "%" + topicMaxLength + "s";
 
     receiver.newConnection("components", bytes -> {
       String message = new String(bytes);
@@ -70,8 +75,10 @@ public class Main {
       PandaLinkState.Orientation tmpOrientation = pls.getOrient();
       PandaLinkState.TwistLinear tmpTwistLinear = pls.getTl();
       PandaLinkState.TwistAngular tmpTwistAngular = pls.getTa();
-      logger.info("{}: pos({},{},{}), orient({},{},{},{})," +
-              " twist-linear({},{},{}), twist-angular({},{},{})",
+      // panda::panda_link0: pos(-3.0621333E-8,-1.5197388E-8,3.3411725E-5), orient(0.0,0.0,0.0,0.0), twist-linear(0.0,0.0,0.0), twist-angular(0.0,0.0,0.0)
+
+      logger.printf(Level.INFO,topicPattern + ": pos(% .5f,% .5f,% .5f), ori(%.1f,%.1f,%.1f,%.1f)," +
+              " twL(%.1f,%.1f,%.1f), twA(%.1f,%.1f,%.1f)",
           pls.getName(),
           tmpPosition.getPositionX(),
           tmpPosition.getPositionY(),
diff --git a/ros2rag.receiverstub/src/main/resources/log4j2.xml b/ros2rag.receiverstub/src/main/resources/log4j2.xml
index 679a7bb0c6171f5366d8283c0a451598fdca17db..ed3f9b7288951b7e2aaa1b25130158cd95d2ed97 100644
--- a/ros2rag.receiverstub/src/main/resources/log4j2.xml
+++ b/ros2rag.receiverstub/src/main/resources/log4j2.xml
@@ -6,7 +6,7 @@
         </Console>
     </Appenders>
     <Loggers>
-        <Root level="debug">
+        <Root level="info">
             <AppenderRef ref="Console"/>
         </Root>
     </Loggers>
diff --git a/ros2rag.senderstub/src/main/java/de/tudresden/inf/st/ros2rag/senderstub/Main.java b/ros2rag.senderstub/src/main/java/de/tudresden/inf/st/ros2rag/senderstub/Main.java
index a2678369be1cdecdc24132e4002b281bdb725e1a..3ae4b473b96411ac632bdbacc4fa0682ed1d7640 100644
--- a/ros2rag.senderstub/src/main/java/de/tudresden/inf/st/ros2rag/senderstub/Main.java
+++ b/ros2rag.senderstub/src/main/java/de/tudresden/inf/st/ros2rag/senderstub/Main.java
@@ -7,34 +7,21 @@ import java.util.concurrent.TimeUnit;
 
 public class Main {
   public static void main(String[] args) throws Exception {
-    final String topic;
-    final byte[] message;
-
-    if (args.length < 1) {
-      topic = "robot/joint1";
-    } else {
-      topic = args[0];
-    }
-
-    if (args.length < 2) {
-      Linkstate.PandaLinkState pls = Linkstate.PandaLinkState.newBuilder()
-          .setName("Joint1")
-          .setPos(Linkstate.PandaLinkState.Position.newBuilder()
-              .setPositionX(0.5f)
-              .setPositionY(0.5f)
-              .setPositionZ(0.5f)
-              .build())
-          .setOrient(Linkstate.PandaLinkState.Orientation.newBuilder()
-              .setOrientationX(0)
-              .setOrientationY(0)
-              .setOrientationZ(0)
-              .setOrientationW(0)
-              .build())
-          .build();
-      message = pls.toByteArray();
-    } else {
-      message = args[1].getBytes();
+    // assume 4 arguments
+    if (args.length < 4) {
+      System.err.println("Sends a new position, expected arguments: topic x y z");
+      return;
     }
+    final String topic = args[0];
+    Linkstate.PandaLinkState pls = Linkstate.PandaLinkState.newBuilder()
+        .setName(args[0])
+        .setPos(Linkstate.PandaLinkState.Position.newBuilder()
+            .setPositionX(Float.parseFloat(args[1]))
+            .setPositionY(Float.parseFloat(args[2]))
+            .setPositionZ(Float.parseFloat(args[3]))
+            .build())
+        .build();
+    final byte[] message = pls.toByteArray();
 
     MqttUpdater sender = new MqttUpdater("sender stub").dontSendWelcomeMessage();
     sender.setHost("localhost", 1883);
diff --git a/ros2rag.starter/src/main/jastadd/Computation.jrag b/ros2rag.starter/src/main/jastadd/Computation.jrag
index 09c151228c5c3b4811566bc0172e1e83247f648f..bddac5a4db5245259f19cfb72c86b9533fbf3fa7 100644
--- a/ros2rag.starter/src/main/jastadd/Computation.jrag
+++ b/ros2rag.starter/src/main/jastadd/Computation.jrag
@@ -1,5 +1,6 @@
 aspect Computation {
   syn boolean RobotArm.isInSafetyZone() {
+    System.out.println("isInSafetyZone()");
     for (Joint joint : getJointList()) {
       if (model().getZoneModel().isInSafetyZone(joint.getCurrentPosition())) {
         return true;
@@ -10,15 +11,10 @@ aspect Computation {
 
   cache ZoneModel.isInSafetyZone(IntPosition pos);
   syn boolean ZoneModel.isInSafetyZone(IntPosition pos) {
+    System.out.println("isInSafetyZone(" + pos + ")");
     for (Zone sz : getSafetyZoneList()) {
       for (Coordinate coordinate : sz.getCoordinateList()) {
-        IntPosition inside = coordinate.getPosition();
-        if (inside.getX() <= pos.getX() &&
-              inside.getY() <= pos.getY() &&
-              inside.getZ() <= pos.getZ() &&
-              pos.getX() <= inside.getX() + getSize().getX() &&
-              pos.getY() <= inside.getY() + getSize().getY() &&
-              pos.getZ() <= inside.getZ() + getSize().getZ()) {
+        if (coordinate.getPosition().equals(pos)) {
           return true;
         }
       }
diff --git a/ros2rag.starter/src/main/jastadd/Definitions.ros2rag b/ros2rag.starter/src/main/jastadd/Definitions.ros2rag
index da9ce33855f7e8cb4a17bf27fa930b407e84b066..60e7d1923a9a0a735bc4c8c3718ddd1c8e5b7ed7 100644
--- a/ros2rag.starter/src/main/jastadd/Definitions.ros2rag
+++ b/ros2rag.starter/src/main/jastadd/Definitions.ros2rag
@@ -10,20 +10,23 @@ RobotArm.AppropriateSpeed canDependOn Joint.CurrentPosition as dependency1 ;
 
 // --- mapping definitions ---
 ParseLinkState maps byte[] bytes to panda.Linkstate.PandaLinkState {:
+//  System.out.println("ParseLinkState");
   return panda.Linkstate.PandaLinkState.parseFrom(bytes);
 :}
 
 SerializeRobotConfig maps config.Robotconfig.RobotConfig rc to byte[] {:
+//  System.out.println("SerializeRobotConfig");
   return rc.toByteArray();
 :}
 
 LinkStateToIntPosition maps panda.Linkstate.PandaLinkState pls to IntPosition {:
+//  System.out.println("LinkStateToIntPosition");
   panda.Linkstate.PandaLinkState.Position p = pls.getPos();
-  { int i = 0; }
-  return IntPosition.of((int) p.getPositionX(), (int) p.getPositionY(), (int) p.getPositionZ());
+  return IntPosition.of((int) (Math.round(p.getPositionX() * 2)), (int) (Math.round(p.getPositionY() * 2)), (int) (Math.round(p.getPositionZ() * 2 + 0.5)));
 :}
 
 CreateSpeedMessage maps double speed to config.Robotconfig.RobotConfig {:
+//  System.out.println("CreateSpeedMessage");
   return config.Robotconfig.RobotConfig.newBuilder()
     .setSpeed(speed)
     .setLoopTrajectory(true)
diff --git a/ros2rag.starter/src/main/jastadd/RobotModel.relast b/ros2rag.starter/src/main/jastadd/RobotModel.relast
index ea347d0ec303a8d3555cd0118d403b53664ffe33..415d3e5e25b8d03fa1d53bc6f95c05ad5e1987d8 100644
--- a/ros2rag.starter/src/main/jastadd/RobotModel.relast
+++ b/ros2rag.starter/src/main/jastadd/RobotModel.relast
@@ -1,6 +1,6 @@
 Model ::= RobotArm ZoneModel ;
 
-ZoneModel ::= <Size:IntPosition> SafetyZone:Zone* ;
+ZoneModel ::= SafetyZone:Zone* ;
 
 Zone ::= Coordinate* ;
 
diff --git a/ros2rag.starter/src/main/java/de/tudresden/inf/st/ros2rag/starter/StarterMain.java b/ros2rag.starter/src/main/java/de/tudresden/inf/st/ros2rag/starter/StarterMain.java
index 45d7f7f73ec414f8c3e9bfb6f3e2ccd13132188d..66a48f60b39cb7c5d48af9993eeca7159dc92e51 100644
--- a/ros2rag.starter/src/main/java/de/tudresden/inf/st/ros2rag/starter/StarterMain.java
+++ b/ros2rag.starter/src/main/java/de/tudresden/inf/st/ros2rag/starter/StarterMain.java
@@ -28,9 +28,8 @@ public class StarterMain {
     File configFile = new File(args[0]);
 
     final int[][] safetyZoneCoordinates = {
-        {0, 0, 0},
-        {-1, 0, 0},
-        {1, 0, 0}
+        {1, 1, 0},
+        {-1, -1, 1}
     };
 
     // --- No configuration below this line ---
@@ -43,7 +42,6 @@ public class StarterMain {
     model.MqttSetHost(config.mqttHost);
 
     ZoneModel zoneModel = new ZoneModel();
-    zoneModel.setSize(makePosition(1, 1, 1));
 
     Zone safetyZone = new Zone();
     for (int[] coordinate : safetyZoneCoordinates) {
@@ -103,9 +101,9 @@ public class StarterMain {
         .setEnableOrientation(false)
         .setEnableTwistAngular(false)
         .setEnableTwistLinear(false)
-        .setPublishRate(1000)
+        .setPublishRate(100)
         .build();
-    mainHandler.publish(dataConfigTopic, dataConfig.toByteArray());
+    mainHandler.publish(dataConfigTopic, dataConfig.toByteArray(), true);
   }
 
   private void logStatus(String prefix, RobotArm robotArm) {
diff --git a/ros2rag.starter/src/main/resources/config.json b/ros2rag.starter/src/main/resources/config.json
index af5e2b9835bd31e60418d97a8162f0c2fdb82215..ad12193a6c0da29e9e8ac097e615038933b1c92f 100644
--- a/ros2rag.starter/src/main/resources/config.json
+++ b/ros2rag.starter/src/main/resources/config.json
@@ -1,15 +1,15 @@
 {
   "joints": [
-    {"name": "Joint0", "topic": "panda_link_0"},
-    {"name": "Joint1", "topic": "panda_link_1"},
-    {"name": "Joint2", "topic": "panda_link_2"},
-    {"name": "Joint3", "topic": "panda_link_3"},
-    {"name": "Joint4", "topic": "panda_link_4"},
-    {"name": "Joint5", "topic": "panda_link_5"},
-    {"name": "Joint6", "topic": "panda_link_6"},
-    {"name": "EndEffector", "topic": "panda_link_7", "isEndEffector": true},
-    {"name": "LeftFinger", "topic": "panda_link_8"},
-    {"name": "RightFinger", "topic": "panda_link_9"}
+    {"name": "Joint0", "topic": "panda::panda_link0"},
+    {"name": "Joint1", "topic": "panda::panda_link1"},
+    {"name": "Joint2", "topic": "panda::panda_link2"},
+    {"name": "Joint3", "topic": "panda::panda_link3"},
+    {"name": "Joint4", "topic": "panda::panda_link4"},
+    {"name": "Joint5", "topic": "panda::panda_link5"},
+    {"name": "Joint6", "topic": "panda::panda_link6"},
+    {"name": "EndEffector", "topic": "panda::panda_link7", "isEndEffector": true},
+    {"name": "LeftFinger", "topic": "panda::panda_leftfinger"},
+    {"name": "RightFinger", "topic": "panda::panda_rightfinger"}
   ],
   "robotConfigTopic": "robotconfig",
   "dataConfigTopic": "dataconfig"
diff --git a/ros2rag.tests/build.gradle b/ros2rag.tests/build.gradle
index 20906aa9cb5f938b5bc3f15c7b3d51a1135462f3..568f441a595ac78644f2d216c850ab92ef8660d9 100644
--- a/ros2rag.tests/build.gradle
+++ b/ros2rag.tests/build.gradle
@@ -30,11 +30,22 @@ dependencies {
 }
 
 test {
-    useJUnitPlatform()
+    useJUnitPlatform {
+        excludeTags 'mqtt'
+    }
 
     maxHeapSize = '1G'
 }
 
+task allTests(type: Test, dependsOn: testClasses) {
+    description = 'Run every test'
+    group = 'verification'
+
+    useJUnitPlatform {
+        includeTags 'mqtt'
+    }
+}
+
 relastTest {
     compilerLocation = '../libs/relast.jar'
 }
@@ -82,7 +93,7 @@ task compileExampleTest(type: RelastTest) {
             'src/test/02-after-ros2rag/example/ROS2RAG.jadd'
 }
 
-test.dependsOn compileExampleTest
+testClasses.dependsOn compileExampleTest
 compileExampleTest.dependsOn preprocessExampleTest
 
 // --- Test: default-only-read ---
@@ -112,7 +123,7 @@ task compileDefaultOnlyReadTest(type: RelastTest) {
             'src/test/02-after-ros2rag/defaultOnlyRead/ROS2RAG.jadd'
 }
 
-test.dependsOn compileDefaultOnlyReadTest
+testClasses.dependsOn compileDefaultOnlyReadTest
 compileDefaultOnlyReadTest.dependsOn preprocessDefaultOnlyReadTest
 
 // --- Test: default-only-write ---
@@ -143,7 +154,7 @@ task compileDefaultOnlyWriteTest(type: RelastTest) {
             'src/test/02-after-ros2rag/defaultOnlyWrite/ROS2RAG.jadd'
 }
 
-test.dependsOn compileDefaultOnlyWriteTest
+testClasses.dependsOn compileDefaultOnlyWriteTest
 compileDefaultOnlyWriteTest.dependsOn preprocessDefaultOnlyWriteTest
 
 // --- Test: read1write2 ---
@@ -174,7 +185,7 @@ task compileRead1Write2Test(type: RelastTest) {
             'src/test/02-after-ros2rag/read1write2/ROS2RAG.jadd'
 }
 
-test.dependsOn compileRead1Write2Test
+testClasses.dependsOn compileRead1Write2Test
 compileRead1Write2Test.dependsOn preprocessRead1Write2Test
 
 // --- Test: read2write1 ---
@@ -205,7 +216,7 @@ task compileRead2Write1Test(type: RelastTest) {
             'src/test/02-after-ros2rag/read2write1/ROS2RAG.jadd'
 }
 
-test.dependsOn compileRead2Write1Test
+testClasses.dependsOn compileRead2Write1Test
 compileRead2Write1Test.dependsOn preprocessRead2Write1Test
 
 clean {
diff --git a/ros2rag.tests/src/test/01-input/example/Test.jadd b/ros2rag.tests/src/test/01-input/example/Test.jadd
index 53f41b6b1d5f7c181d0e4ea06a34ab8240eb82d1..c355f57814c9def67657a148a5a28061e2569d28 100644
--- a/ros2rag.tests/src/test/01-input/example/Test.jadd
+++ b/ros2rag.tests/src/test/01-input/example/Test.jadd
@@ -1,4 +1,16 @@
 aspect GrammarTypes {
+  public class TestCounter {
+    public static TestCounter INSTANCE = new TestCounter();
+    public int numberParseLinkState = 0;
+    public int numberSerializeRobotConfig = 0;
+    public int numberLinkStateToIntPosition = 0;
+    public int numberCreateSpeedMessage = 0;
+    public int numberInSafetyZone = 0;
+    public static void reset() {
+      TestCounter.INSTANCE = new TestCounter();
+    }
+  }
+
   public class IntPosition {
     private final int x, y, z;
 
@@ -53,6 +65,7 @@ aspect GrammarTypes {
   eq RobotArm.getEndEffector().containingRobotArm() = this;
 
   syn boolean RobotArm.isInSafetyZone() {
+    TestCounter.INSTANCE.numberInSafetyZone += 1;
     for (Joint joint : getJointList()) {
       if (model().getZoneModel().isInSafetyZone(joint.getCurrentPosition())) {
         return true;
diff --git a/ros2rag.tests/src/test/01-input/example/Test.relast b/ros2rag.tests/src/test/01-input/example/Test.relast
index c6b346d5df0db5b333141202a800b5951643eef2..ea347d0ec303a8d3555cd0118d403b53664ffe33 100644
--- a/ros2rag.tests/src/test/01-input/example/Test.relast
+++ b/ros2rag.tests/src/test/01-input/example/Test.relast
@@ -4,7 +4,7 @@ ZoneModel ::= <Size:IntPosition> SafetyZone:Zone* ;
 
 Zone ::= Coordinate* ;
 
-RobotArm ::= Joint* EndEffector <AttributeTestSource:int> /<AppropriateSpeed:double>/ ;
+RobotArm ::= Joint* EndEffector /<AppropriateSpeed:double>/ ;
 
 Joint ::= <Name:String> <CurrentPosition:IntPosition> ;
 
diff --git a/ros2rag.tests/src/test/01-input/example/Test.ros2rag b/ros2rag.tests/src/test/01-input/example/Test.ros2rag
index 9168da25b6d98b26784ef67b64c515be6cecf76c..5c929386c01052f3e9cfc2bcd81fe45482ea8446 100644
--- a/ros2rag.tests/src/test/01-input/example/Test.ros2rag
+++ b/ros2rag.tests/src/test/01-input/example/Test.ros2rag
@@ -5,24 +5,26 @@ write RobotArm.AppropriateSpeed using CreateSpeedMessage, SerializeRobotConfig ;
 
 // --- dependency definitions ---
 RobotArm.AppropriateSpeed canDependOn Joint.CurrentPosition as dependency1 ;
-RobotArm.AppropriateSpeed canDependOn RobotArm.AttributeTestSource as dependency2 ;
 
 // --- mapping definitions ---
 ParseLinkState maps byte[] bytes to panda.Linkstate.PandaLinkState {:
+  TestCounter.INSTANCE.numberParseLinkState += 1;
   return panda.Linkstate.PandaLinkState.parseFrom(bytes);
 :}
 
 SerializeRobotConfig maps config.Robotconfig.RobotConfig rc to byte[] {:
+  TestCounter.INSTANCE.numberSerializeRobotConfig += 1;
   return rc.toByteArray();
 :}
 
 LinkStateToIntPosition maps panda.Linkstate.PandaLinkState pls to IntPosition {:
+  TestCounter.INSTANCE.numberLinkStateToIntPosition += 1;
   panda.Linkstate.PandaLinkState.Position p = pls.getPos();
-  { int i = 0; }
-  return IntPosition.of((int) p.getPositionX(), (int) p.getPositionY(), (int) p.getPositionZ());
+  return IntPosition.of((int) (10 * p.getPositionX()), (int) (10 * p.getPositionY()), (int) (10 * p.getPositionZ()));
 :}
 
 CreateSpeedMessage maps double speed to config.Robotconfig.RobotConfig {:
+  TestCounter.INSTANCE.numberCreateSpeedMessage += 1;
   return config.Robotconfig.RobotConfig.newBuilder()
     .setSpeed(speed)
     .build();
diff --git a/ros2rag.tests/src/test/java/org/jastadd/ros2rag/tests/AbstractMqttTest.java b/ros2rag.tests/src/test/java/org/jastadd/ros2rag/tests/AbstractMqttTest.java
index 3ca2c2dd10db9c7314e86582a02c3d705af2c2f4..6c07780f93ed5f01598bb995c337df765b9256d0 100644
--- a/ros2rag.tests/src/test/java/org/jastadd/ros2rag/tests/AbstractMqttTest.java
+++ b/ros2rag.tests/src/test/java/org/jastadd/ros2rag/tests/AbstractMqttTest.java
@@ -2,6 +2,7 @@ package org.jastadd.ros2rag.tests;
 
 import defaultOnlyRead.ast.MqttUpdater;
 import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Tag;
 
 import java.io.IOException;
 import java.util.concurrent.TimeUnit;
@@ -11,6 +12,7 @@ import java.util.concurrent.TimeUnit;
  *
  * @author rschoene - Initial contribution
  */
+@Tag("mqtt")
 public abstract class AbstractMqttTest {
   static boolean checkDone = false;
   static Boolean checkResult;
diff --git a/ros2rag.tests/src/test/java/org/jastadd/ros2rag/tests/DefaultOnlyReadTest.java b/ros2rag.tests/src/test/java/org/jastadd/ros2rag/tests/DefaultOnlyReadTest.java
index 3594cfd1a946ba42f65def205aa821354dba50e2..c82e9ca1e1642dbf9589760a03f8d2164a5ff424 100644
--- a/ros2rag.tests/src/test/java/org/jastadd/ros2rag/tests/DefaultOnlyReadTest.java
+++ b/ros2rag.tests/src/test/java/org/jastadd/ros2rag/tests/DefaultOnlyReadTest.java
@@ -106,7 +106,7 @@ public class DefaultOnlyReadTest extends AbstractMqttTest {
     sender.publish(TOPIC_BOXED_DOUBLE, ByteBuffer.allocate(8).putDouble(expectedDoubleValue).array());
     sender.publish(TOPIC_BOXED_CHARACTER, ByteBuffer.allocate(2).putChar(expectedCharValue).array());
 
-    TimeUnit.SECONDS.sleep(2);
+    TestUtils.waitForMqtt();
 
     assertEquals(expectedIntValue, integers.getIntValue());
     assertEquals(expectedShortValue, integers.getShortValue());
diff --git a/ros2rag.tests/src/test/java/org/jastadd/ros2rag/tests/DefaultOnlyWriteTest.java b/ros2rag.tests/src/test/java/org/jastadd/ros2rag/tests/DefaultOnlyWriteTest.java
index df4a22f5835df09a407595392a4969e7af9ee6f2..a48228e0ad13555016cb86b5de87fe00fcf88fdb 100644
--- a/ros2rag.tests/src/test/java/org/jastadd/ros2rag/tests/DefaultOnlyWriteTest.java
+++ b/ros2rag.tests/src/test/java/org/jastadd/ros2rag/tests/DefaultOnlyWriteTest.java
@@ -67,21 +67,21 @@ public class DefaultOnlyWriteTest extends AbstractMqttTest {
     setupReceiverAndConnect(true);
 
     // check initial value
-    TimeUnit.SECONDS.sleep(2);
+    TestUtils.waitForMqtt();
     checkData(1, 1, 1.1, 'a', "ab");
 
     // set new value
     setData("2", "2.2", "cd");
 
     // check new value
-    TimeUnit.SECONDS.sleep(2);
+    TestUtils.waitForMqtt();
     checkData(2, 2, 2.2, 'c', "cd");
 
     // set new value
     setData("3", "3.2", "ee");
 
     // check new value
-    TimeUnit.SECONDS.sleep(2);
+    TestUtils.waitForMqtt();
     checkData(3, 3, 3.2, 'e', "ee");
   }
 
@@ -92,21 +92,21 @@ public class DefaultOnlyWriteTest extends AbstractMqttTest {
     setupReceiverAndConnect(false);
 
     // check initial value (will be default values)
-    TimeUnit.SECONDS.sleep(2);
+    TestUtils.waitForMqtt();
     checkData(0, null, null, null, null);
 
     // set new value
     setData("2", "2.2", "cd");
 
     // check new value
-    TimeUnit.SECONDS.sleep(2);
+    TestUtils.waitForMqtt();
     checkData(1, 2, 2.2, 'c', "cd");
 
     // set new value
     setData("3", "3.2", "ee");
 
     // check new value
-    TimeUnit.SECONDS.sleep(2);
+    TestUtils.waitForMqtt();
     checkData(2, 3, 3.2, 'e', "ee");
   }
 
diff --git a/ros2rag.tests/src/test/java/org/jastadd/ros2rag/tests/ExampleTest.java b/ros2rag.tests/src/test/java/org/jastadd/ros2rag/tests/ExampleTest.java
index c22861e996826eaf720a33fe9e0c02f7b110978e..98d8e8b58aa5015babe0d3e20c3c948c9aa55c7c 100644
--- a/ros2rag.tests/src/test/java/org/jastadd/ros2rag/tests/ExampleTest.java
+++ b/ros2rag.tests/src/test/java/org/jastadd/ros2rag/tests/ExampleTest.java
@@ -4,11 +4,11 @@ import com.google.protobuf.InvalidProtocolBufferException;
 import config.Robotconfig.RobotConfig;
 import example.ast.*;
 import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
+import panda.Linkstate;
 
 import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
 import java.util.concurrent.TimeUnit;
 
 import static org.junit.jupiter.api.Assertions.*;
@@ -22,16 +22,24 @@ public class ExampleTest extends AbstractMqttTest {
 
   private static final String TOPIC_CONFIG = "robot/config";
   private static final String TOPIC_JOINT1 = "robot/arm/joint1";
+  private static final String TOPIC_JOINT2 = "robot/arm/joint2";
 
   private Model model;
   private RobotArm robotArm;
   private Joint joint1;
-  private MqttUpdater receiver;
+  private Joint joint2;
+  private MqttUpdater handler;
+  private ReceiverData data;
+
+  @BeforeEach
+  public void resetTestCounter() {
+    TestCounter.reset();
+  }
 
   @AfterEach
   public void closeConnections() {
-    if (receiver != null) {
-      receiver.close();
+    if (handler != null) {
+      handler.close();
     }
     if (model != null) {
       model.MqttCloseConnections();
@@ -44,49 +52,194 @@ public class ExampleTest extends AbstractMqttTest {
   }
 
   @Test
-  public void communicate() throws IOException, InterruptedException {
+  public void communicateSendInitialValue() throws IOException, InterruptedException {
     createModel();
-    List<RobotConfig> receivedConfigs = new ArrayList<>();
-
-    createListener(receivedConfigs);
+    setupReceiverAndConnect(true);
 
-    model.MqttSetHost(TestUtils.getMqttHost());
-    assertTrue(model.MqttWaitUntilReady(2, TimeUnit.SECONDS));
     // joint is currently within the safety zone, so speed should be low
-    robotArm.connectAppropriateSpeed(TOPIC_CONFIG, true);
-    joint1.connectCurrentPosition(TOPIC_JOINT1);
+    TestUtils.waitForMqtt();
+    assertEquals(0, TestCounter.INSTANCE.numberParseLinkState);
+    assertEquals(0, TestCounter.INSTANCE.numberLinkStateToIntPosition);
+    assertEquals(1, TestCounter.INSTANCE.numberInSafetyZone);
+    assertEquals(1, TestCounter.INSTANCE.numberCreateSpeedMessage);
+    assertEquals(1, TestCounter.INSTANCE.numberSerializeRobotConfig);
+    assertEquals(1, data.numberOfConfigs);
+    assertFalse(data.failedLastConversion);
+    assertEquals(robotArm.speedLow(), data.lastConfig.getSpeed(), TestUtils.DELTA);
 
-    // now change the position of the joint out of the safety zone
-    joint1.setCurrentPosition(makePosition(2, 2, 2));
+    // change position of the first joint out of the safety zone, second still in
+    sendData(TOPIC_JOINT1, 0.2f, 0.2f, 0.2f);
 
-    // and wait for MQTT to send/receive messages
-    TimeUnit.SECONDS.sleep(2);
+    // still in safety zone, so no update should have been sent
+    TestUtils.waitForMqtt();
+    assertEquals(makePosition(2, 2, 2), joint1.getCurrentPosition());
+    assertEquals(1, TestCounter.INSTANCE.numberParseLinkState);
+    assertEquals(1, TestCounter.INSTANCE.numberLinkStateToIntPosition);
+    assertEquals(2, TestCounter.INSTANCE.numberInSafetyZone);
+    assertEquals(2, TestCounter.INSTANCE.numberCreateSpeedMessage);
+    assertEquals(2, TestCounter.INSTANCE.numberSerializeRobotConfig);
+    assertEquals(1, data.numberOfConfigs);
+    assertFalse(data.failedLastConversion);
+    assertEquals(robotArm.speedLow(), data.lastConfig.getSpeed(), TestUtils.DELTA);
 
-    // there should be two configs received by now (the initial one, and the updated)
-    assertEquals(2, receivedConfigs.size());
-    RobotConfig actualInitialConfig = receivedConfigs.get(0);
-    assertEquals(robotArm.speedLow(), actualInitialConfig.getSpeed(), TestUtils.DELTA);
+    // change position of second joint also out of the safety zone, now speed must be high
+    sendData(TOPIC_JOINT2, 0.3f, 0.4f, 0.5f);
 
-    RobotConfig actualUpdatedConfig = receivedConfigs.get(1);
-    assertEquals(robotArm.speedHigh(), actualUpdatedConfig.getSpeed(), TestUtils.DELTA);
+    TestUtils.waitForMqtt();
+    assertEquals(makePosition(3, 4, 5), joint2.getCurrentPosition());
+    assertEquals(2, TestCounter.INSTANCE.numberParseLinkState);
+    assertEquals(2, TestCounter.INSTANCE.numberLinkStateToIntPosition);
+    assertEquals(3, TestCounter.INSTANCE.numberInSafetyZone);
+    assertEquals(3, TestCounter.INSTANCE.numberCreateSpeedMessage);
+    assertEquals(3, TestCounter.INSTANCE.numberSerializeRobotConfig);
+    assertEquals(2, data.numberOfConfigs);
+    assertFalse(data.failedLastConversion);
+    assertEquals(robotArm.speedHigh(), data.lastConfig.getSpeed(), TestUtils.DELTA);
+
+    // change position of second joint, no change after mapping
+    sendData(TOPIC_JOINT2, 0.33f, 0.42f, 0.51f);
+
+    TestUtils.waitForMqtt();
+    assertEquals(makePosition(3, 4, 5), joint2.getCurrentPosition());
+    assertEquals(3, TestCounter.INSTANCE.numberParseLinkState);
+    assertEquals(3, TestCounter.INSTANCE.numberLinkStateToIntPosition);
+    assertEquals(3, TestCounter.INSTANCE.numberInSafetyZone);
+    assertEquals(3, TestCounter.INSTANCE.numberCreateSpeedMessage);
+    assertEquals(3, TestCounter.INSTANCE.numberSerializeRobotConfig);
+    assertEquals(2, data.numberOfConfigs);
+    assertFalse(data.failedLastConversion);
+
+    // change position of second joint, still out of the safety zone, no update should be sent
+    sendData(TOPIC_JOINT2, 1.3f, 2.4f, 3.5f);
+
+    TestUtils.waitForMqtt();
+    assertEquals(makePosition(13, 24, 35), joint2.getCurrentPosition());
+    assertEquals(4, TestCounter.INSTANCE.numberParseLinkState);
+    assertEquals(4, TestCounter.INSTANCE.numberLinkStateToIntPosition);
+    assertEquals(4, TestCounter.INSTANCE.numberInSafetyZone);
+    assertEquals(4, TestCounter.INSTANCE.numberCreateSpeedMessage);
+    assertEquals(4, TestCounter.INSTANCE.numberSerializeRobotConfig);
+    assertEquals(2, data.numberOfConfigs);
+    assertFalse(data.failedLastConversion);
   }
 
-  private void createListener(List<RobotConfig> receivedConfigs) {
-    receiver = new MqttUpdater("receiver");
-    try {
-      receiver.setHost(TestUtils.getMqttHost(), TestUtils.getMqttDefaultPort());
-    } catch (IOException e) {
-      fail("Could not set host: " + e.getMessage());
-    }
-    assertTrue(receiver.waitUntilReady(2, TimeUnit.SECONDS));
-    receiver.newConnection(TOPIC_CONFIG, bytes -> {
+  @Test
+  public void communicateOnlyUpdatedValue() throws IOException, InterruptedException {
+    createModel();
+    setupReceiverAndConnect(false);
+
+    // no value should have been sent
+    TestUtils.waitForMqtt();
+    assertEquals(0, TestCounter.INSTANCE.numberParseLinkState);
+    assertEquals(0, TestCounter.INSTANCE.numberLinkStateToIntPosition);
+    assertEquals(1, TestCounter.INSTANCE.numberInSafetyZone);
+    assertEquals(1, TestCounter.INSTANCE.numberCreateSpeedMessage);
+    assertEquals(1, TestCounter.INSTANCE.numberSerializeRobotConfig);
+    assertEquals(0, data.numberOfConfigs);
+
+    // change position of the first joint out of the safety zone, second still in
+    sendData(TOPIC_JOINT1, 0.2f, 0.2f, 0.2f);
+
+    // still in safety zone, hence, no value should have been sent
+    TestUtils.waitForMqtt();
+    assertEquals(makePosition(2, 2, 2), joint1.getCurrentPosition());
+    assertEquals(1, TestCounter.INSTANCE.numberParseLinkState);
+    assertEquals(1, TestCounter.INSTANCE.numberLinkStateToIntPosition);
+    assertEquals(2, TestCounter.INSTANCE.numberInSafetyZone);
+    assertEquals(2, TestCounter.INSTANCE.numberCreateSpeedMessage);
+    assertEquals(2, TestCounter.INSTANCE.numberSerializeRobotConfig);
+    assertEquals(0, data.numberOfConfigs);
+
+    // change position of second joint also out of the safety zone, now speed must be high
+    sendData(TOPIC_JOINT2, 0.3f, 0.4f, 0.5f);
+
+    TestUtils.waitForMqtt();
+    assertEquals(makePosition(3, 4, 5), joint2.getCurrentPosition());
+    assertEquals(2, TestCounter.INSTANCE.numberParseLinkState);
+    assertEquals(2, TestCounter.INSTANCE.numberLinkStateToIntPosition);
+    assertEquals(3, TestCounter.INSTANCE.numberInSafetyZone);
+    assertEquals(3, TestCounter.INSTANCE.numberCreateSpeedMessage);
+    assertEquals(3, TestCounter.INSTANCE.numberSerializeRobotConfig);
+    assertEquals(1, data.numberOfConfigs);
+    assertFalse(data.failedLastConversion);
+    assertEquals(robotArm.speedHigh(), data.lastConfig.getSpeed(), TestUtils.DELTA);
+
+    // change position of second joint, no change after mapping
+    sendData(TOPIC_JOINT2, 0.33f, 0.42f, 0.51f);
+
+    TestUtils.waitForMqtt();
+    assertEquals(makePosition(3, 4, 5), joint2.getCurrentPosition());
+    assertEquals(3, TestCounter.INSTANCE.numberParseLinkState);
+    assertEquals(3, TestCounter.INSTANCE.numberLinkStateToIntPosition);
+    assertEquals(3, TestCounter.INSTANCE.numberInSafetyZone);
+    assertEquals(3, TestCounter.INSTANCE.numberCreateSpeedMessage);
+    assertEquals(3, TestCounter.INSTANCE.numberSerializeRobotConfig);
+    assertEquals(1, data.numberOfConfigs);
+    assertFalse(data.failedLastConversion);
+
+    // change position of second joint, still out of the safety zone, no update should be sent
+    sendData(TOPIC_JOINT2, 1.3f, 2.4f, 3.5f);
+
+    TestUtils.waitForMqtt();
+    assertEquals(makePosition(13, 24, 35), joint2.getCurrentPosition());
+    assertEquals(4, TestCounter.INSTANCE.numberParseLinkState);
+    assertEquals(4, TestCounter.INSTANCE.numberLinkStateToIntPosition);
+    assertEquals(4, TestCounter.INSTANCE.numberInSafetyZone);
+    assertEquals(4, TestCounter.INSTANCE.numberCreateSpeedMessage);
+    assertEquals(4, TestCounter.INSTANCE.numberSerializeRobotConfig);
+    assertEquals(1, data.numberOfConfigs);
+    assertFalse(data.failedLastConversion);
+  }
+
+  @Test
+  public void testFailedConversion() throws IOException {
+    createModel();
+    setupReceiverAndConnect(false);
+
+    handler.publish(TOPIC_JOINT1, "not-a-pandaLinkState".getBytes());
+    assertEquals(0, data.numberOfConfigs);
+    assertTrue(data.failedLastConversion);
+  }
+
+  private void sendData(String topic, float x, float y, float z) {
+    handler.publish(topic, Linkstate.PandaLinkState.newBuilder()
+        .setPos(Linkstate.PandaLinkState.Position.newBuilder()
+            .setPositionX(x)
+            .setPositionY(y)
+            .setPositionZ(z)
+            .build())
+        .build()
+        .toByteArray()
+    );
+  }
+
+  private void setupReceiverAndConnect(boolean writeCurrentValue) throws IOException {
+    model.MqttSetHost(TestUtils.getMqttHost());
+    assertTrue(model.MqttWaitUntilReady(2, TimeUnit.SECONDS));
+
+    handler = new MqttUpdater().dontSendWelcomeMessage().setHost(TestUtils.getMqttHost());
+    assertTrue(handler.waitUntilReady(2, TimeUnit.SECONDS));
+
+    // add dependencies
+    robotArm.addDependency1(joint1);
+    robotArm.addDependency1(joint2);
+    robotArm.addDependency1(robotArm.getEndEffector());
+
+    data = new ReceiverData();
+
+    handler.newConnection(TOPIC_CONFIG, bytes -> {
+      data.numberOfConfigs += 1;
       try {
-        RobotConfig config = RobotConfig.parseFrom(bytes);
-        receivedConfigs.add(config);
+        data.lastConfig = RobotConfig.parseFrom(bytes);
+        data.failedLastConversion = false;
       } catch (InvalidProtocolBufferException e) {
-        fail("Received bad config: " + e.getMessage());
+        data.failedLastConversion = true;
       }
     });
+
+    robotArm.connectAppropriateSpeed(TOPIC_CONFIG, writeCurrentValue);
+    joint1.connectCurrentPosition(TOPIC_JOINT1);
+    joint2.connectCurrentPosition(TOPIC_JOINT2);
   }
 
   private void createModel() {
@@ -95,41 +248,44 @@ public class ExampleTest extends AbstractMqttTest {
     ZoneModel zoneModel = new ZoneModel();
     zoneModel.setSize(makePosition(1, 1, 1));
 
-    IntPosition myPosition = makePosition(0, 0, 0);
-    Coordinate myCoordinate = new Coordinate(myPosition);
-    Coordinate leftPosition = new Coordinate(makePosition(-1, 0, 0));
-    Coordinate rightPosition = new Coordinate(makePosition(1, 0, 0));
+    IntPosition firstPosition = makePosition(0, 0, 0);
+    IntPosition secondPosition = makePosition(-1, 0, 0);
+    IntPosition thirdPosition = makePosition(1, 0, 0);
 
     Zone safetyZone = new Zone();
-    safetyZone.addCoordinate(myCoordinate);
-    safetyZone.addCoordinate(leftPosition);
-    safetyZone.addCoordinate(rightPosition);
+    safetyZone.addCoordinate(new Coordinate(firstPosition));
+    safetyZone.addCoordinate(new Coordinate(secondPosition));
+    safetyZone.addCoordinate(new Coordinate(thirdPosition));
     zoneModel.addSafetyZone(safetyZone);
     model.setZoneModel(zoneModel);
 
     robotArm = new RobotArm();
-    robotArm.setAttributeTestSource(1);  // set initial value, no trigger
 
     joint1 = new Joint();
     joint1.setName("joint1");
-    joint1.setCurrentPosition(myPosition);
+    joint1.setCurrentPosition(firstPosition);
+
+    joint2 = new Joint();
+    joint2.setName("joint2");
+    joint2.setCurrentPosition(secondPosition);
 
     EndEffector endEffector = new EndEffector();
     endEffector.setName("gripper");
     endEffector.setCurrentPosition(makePosition(2, 2, 3));
 
     robotArm.addJoint(joint1);
+    robotArm.addJoint(joint2);
     robotArm.setEndEffector(endEffector);
     model.setRobotArm(robotArm);
-
-    // add dependencies
-    robotArm.addDependency1(joint1);
-    robotArm.addDependency1(endEffector);
-    robotArm.addDependency2(robotArm);
   }
 
   private static IntPosition makePosition(int x, int y, int z) {
     return IntPosition.of(x, y, z);
   }
 
+  private static class ReceiverData {
+    RobotConfig lastConfig;
+    boolean failedLastConversion = true;
+    int numberOfConfigs = 0;
+  }
 }
diff --git a/ros2rag.tests/src/test/java/org/jastadd/ros2rag/tests/Read1Write2Test.java b/ros2rag.tests/src/test/java/org/jastadd/ros2rag/tests/Read1Write2Test.java
index 9c151da32e5ca12c1dea1ec4e23e4c3168ca4910..e57c78a4c3c0a7f4ba9b8fc90e31b8b072f27e97 100644
--- a/ros2rag.tests/src/test/java/org/jastadd/ros2rag/tests/Read1Write2Test.java
+++ b/ros2rag.tests/src/test/java/org/jastadd/ros2rag/tests/Read1Write2Test.java
@@ -59,28 +59,28 @@ public class Read1Write2Test extends AbstractMqttTest {
     setupReceiverAndConnect(true);
 
     // check initial value
-    TimeUnit.SECONDS.sleep(2);
+    TestUtils.waitForMqtt();
     checkData(1, Integer.parseInt(INITIAL_VALUE), prefixed(INITIAL_VALUE), 1, Integer.parseInt(INITIAL_VALUE), prefixed(INITIAL_VALUE));
 
     // set new value
     sendData("2", "3");
 
     // check new value
-    TimeUnit.SECONDS.sleep(2);
+    TestUtils.waitForMqtt();
     checkData(2, 2, prefixed("2"), 2, 3, prefixed("3"));
 
     // set new value
     sendData("4", "4");
 
     // check new value
-    TimeUnit.SECONDS.sleep(2);
+    TestUtils.waitForMqtt();
     checkData(3, 4, prefixed("4"), 3, 4, prefixed("4"));
 
     // set new value only for same
     setDataOnlySame("77");
 
     // check new value
-    TimeUnit.SECONDS.sleep(2);
+    TestUtils.waitForMqtt();
     checkData(4, 77, prefixed("77"), 3, 4, prefixed("4"));
   }
 
@@ -94,28 +94,28 @@ public class Read1Write2Test extends AbstractMqttTest {
     setupReceiverAndConnect(false);
 
     // check initial value
-    TimeUnit.SECONDS.sleep(2);
+    TestUtils.waitForMqtt();
     checkData(0, null, null, 0, null, null);
 
     // set new value
     sendData("2", "3");
 
     // check new value
-    TimeUnit.SECONDS.sleep(2);
+    TestUtils.waitForMqtt();
     checkData(1, 2, prefixed("2"), 1, 3, prefixed("3"));
 
     // set new value
     sendData("4", "4");
 
     // check new value
-    TimeUnit.SECONDS.sleep(2);
+    TestUtils.waitForMqtt();
     checkData(2, 4, prefixed("4"), 2, 4, prefixed("4"));
 
     // set new value only for same
     setDataOnlySame("77");
 
     // check new value
-    TimeUnit.SECONDS.sleep(2);
+    TestUtils.waitForMqtt();
     checkData(3, 77, prefixed("77"), 2, 4, prefixed("4"));
   }
 
diff --git a/ros2rag.tests/src/test/java/org/jastadd/ros2rag/tests/Read2Write1Test.java b/ros2rag.tests/src/test/java/org/jastadd/ros2rag/tests/Read2Write1Test.java
index 6c2b37b83629b245add25fd35659b7ce0ea0185a..ef741400d6bb86edffe804ef2414361f64046761 100644
--- a/ros2rag.tests/src/test/java/org/jastadd/ros2rag/tests/Read2Write1Test.java
+++ b/ros2rag.tests/src/test/java/org/jastadd/ros2rag/tests/Read2Write1Test.java
@@ -58,7 +58,7 @@ public class Read2Write1Test extends AbstractMqttTest {
     setupReceiverAndConnect(true);
 
     // check initial value
-    TimeUnit.SECONDS.sleep(2);
+    TestUtils.waitForMqtt();
     checkData(1, Integer.parseInt(INITIAL_VALUE + INITIAL_VALUE),
         1, Integer.parseInt(INITIAL_VALUE + INITIAL_VALUE));
 
@@ -66,7 +66,7 @@ public class Read2Write1Test extends AbstractMqttTest {
     sendData(true, "2", true, "3");
 
     // check new value. same: 2, 0. different: 3, 0.
-    TimeUnit.SECONDS.sleep(2);
+    TestUtils.waitForMqtt();
     checkData(2, 20,
         2, 30);
 
@@ -74,7 +74,7 @@ public class Read2Write1Test extends AbstractMqttTest {
     sendData(false, "4", false, "4");
 
     // check new value. same: 2, 4. different: 3, 4.
-    TimeUnit.SECONDS.sleep(2);
+    TestUtils.waitForMqtt();
     checkData(3, 24,
         3, 34);
 
@@ -82,7 +82,7 @@ public class Read2Write1Test extends AbstractMqttTest {
     setDataOnlySame(true, "77");
 
     // check new value. same: 77, 4. different: 3, 4.
-    TimeUnit.SECONDS.sleep(2);
+    TestUtils.waitForMqtt();
     checkData(4, 774,
         3, 34);
   }
@@ -97,7 +97,7 @@ public class Read2Write1Test extends AbstractMqttTest {
     setupReceiverAndConnect(false);
 
     // check initial value
-    TimeUnit.SECONDS.sleep(2);
+    TestUtils.waitForMqtt();
     checkData(0, null,
         0, null);
 
@@ -105,7 +105,7 @@ public class Read2Write1Test extends AbstractMqttTest {
     sendData(true, "2", true, "3");
 
     // check new value. same: 2, 0. different: 3, 0.
-    TimeUnit.SECONDS.sleep(2);
+    TestUtils.waitForMqtt();
     checkData(1, 20,
         1, 30);
 
@@ -113,7 +113,7 @@ public class Read2Write1Test extends AbstractMqttTest {
     sendData(false, "4", false, "4");
 
     // check new value. same: 2, 4. different: 3, 4.
-    TimeUnit.SECONDS.sleep(2);
+    TestUtils.waitForMqtt();
     checkData(2, 24,
         2, 34);
 
@@ -121,7 +121,7 @@ public class Read2Write1Test extends AbstractMqttTest {
     setDataOnlySame(true, "77");
 
     // check new value. same: 77, 4. different: 3, 4.
-    TimeUnit.SECONDS.sleep(2);
+    TestUtils.waitForMqtt();
     checkData(3, 774,
         2, 34);
   }
diff --git a/ros2rag.tests/src/test/java/org/jastadd/ros2rag/tests/TestUtils.java b/ros2rag.tests/src/test/java/org/jastadd/ros2rag/tests/TestUtils.java
index d0c03c9221023005f7896abf681f6627609ebed5..05db73e8790a25dd9d730ea637e8be500d568d5f 100644
--- a/ros2rag.tests/src/test/java/org/jastadd/ros2rag/tests/TestUtils.java
+++ b/ros2rag.tests/src/test/java/org/jastadd/ros2rag/tests/TestUtils.java
@@ -5,6 +5,7 @@ import java.io.IOException;
 import java.nio.charset.Charset;
 import java.nio.file.Files;
 import java.nio.file.Paths;
+import java.util.concurrent.TimeUnit;
 
 /**
  * Utility methods for tests.
@@ -58,4 +59,7 @@ public class TestUtils {
     return new String(encoded, encoding);
   }
 
+  static void waitForMqtt() throws InterruptedException {
+    TimeUnit.SECONDS.sleep(2);
+  }
 }