Repository: eclipse/leshan
Pull request #1262 (2022-05-31 16:34:35)

Use LwM2m Test Object (3442) in our test (instead of home made one)


Patch:
diff --git a/leshan-core/pom.xml b/leshan-core/pom.xml
index dd3320d6..7550295f 100644
--- a/leshan-core/pom.xml
+++ b/leshan-core/pom.xml
@@ -81,6 +81,17 @@ Contributors:
                     </execution>
                 </executions>
             </plugin>
+            <plugin>
+                <artifactId>maven-jar-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <goals>
+                            <!-- some test code is reused in integration-tests -->
+                            <goal>test-jar</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
         </plugins>
     </build>
 </project>
diff --git a/leshan-core/src/main/java/org/eclipse/leshan/core/LwM2mId.java b/leshan-core/src/main/java/org/eclipse/leshan/core/LwM2mId.java
index 8dad4203..efc35610 100644
--- a/leshan-core/src/main/java/org/eclipse/leshan/core/LwM2mId.java
+++ b/leshan-core/src/main/java/org/eclipse/leshan/core/LwM2mId.java
@@ -50,6 +50,12 @@ public interface LwM2mId {
     public static final int SRV_LIFETIME = 1;
     public static final int SRV_BINDING = 7;
 
+    /* ACCESS CONTROL RESOURCES */
+    public static final int ACL_OBJECT_ID = 0;
+    public static final int ACL_OBJECT_INSTANCE_ID = 1;
+    public static final int ACL_ACL = 2;
+    public static final int ACL_ACCESS_CONTROL_OWNER = 3;
+
     /* DEVICE RESOURCES */
 
     public static final int DVC_SUPPORTED_BINDING = 16;


Test patch:
diff --git a/leshan-client-core/src/main/java/org/eclipse/leshan/client/object/LwM2mTestObject.java b/leshan-client-core/src/main/java/org/eclipse/leshan/client/object/LwM2mTestObject.java
index 3cfac1c5..afe0d246 100644
--- a/leshan-client-core/src/main/java/org/eclipse/leshan/client/object/LwM2mTestObject.java
+++ b/leshan-client-core/src/main/java/org/eclipse/leshan/client/object/LwM2mTestObject.java
@@ -49,6 +49,15 @@ import org.slf4j.LoggerFactory;
 public class LwM2mTestObject extends SimpleInstanceEnabler {
     private static final Logger LOG = LoggerFactory.getLogger(LwM2mTestObject.class);
 
+    public static final String INITIAL_STRING_VALUE = "initial value";
+    public static final Long INITIAL_INTEGER_VALUE = 64l;
+    public static final ULong INITIAL_UNSIGNED_INTEGER_VALUE = ULong.valueOf("9223372036854775808");
+    public static final Double INITIAL_FLOAT_VALUE = 3.14159d;
+    public static final Boolean INITIAL_BOOLEAN_VALUE = true;
+    public static final byte[] INITIAL_OPAQUE_VALUE = Hex.decodeHex("0123456789ABCDEF".toCharArray());
+    public static final Date INITIAL_TIME_VALUE = new Date(946684800000l);
+    public static final ObjectLink INITIAL_OBJLINK_VALUE = new ObjectLink(3, 0);
+
     private Random random = new Random(System.currentTimeMillis());
 
     public LwM2mTestObject() {
@@ -59,26 +68,24 @@ public class LwM2mTestObject extends SimpleInstanceEnabler {
         initialValues.put(6, Collections.EMPTY_MAP);
 
         // single
-        initialValues.put(110, "initial value");
-        initialValues.put(120, 64l);
-        initialValues.put(125, ULong.valueOf("9223372036854775808"));
-        initialValues.put(130, 3.14159d);
-        initialValues.put(140, true);
-        initialValues.put(150, Hex.decodeHex("0123456789ABCDEF".toCharArray()));
-        initialValues.put(160, new Date(946684800000l));
-        initialValues.put(170, new ObjectLink(3, 0));
+        initialValues.put(110, INITIAL_STRING_VALUE);
+        initialValues.put(120, INITIAL_INTEGER_VALUE);
+        initialValues.put(125, INITIAL_UNSIGNED_INTEGER_VALUE);
+        initialValues.put(130, INITIAL_FLOAT_VALUE);
+        initialValues.put(140, INITIAL_BOOLEAN_VALUE);
+        initialValues.put(150, INITIAL_OPAQUE_VALUE);
+        initialValues.put(160, INITIAL_TIME_VALUE);
+        initialValues.put(170, INITIAL_OBJLINK_VALUE);
 
         // multi
-        initialValues.put(1110, LwM2mResourceInstance.newStringInstance(0, "initial value"));
-        initialValues.put(1120, LwM2mResourceInstance.newIntegerInstance(0, 64l));
-        initialValues.put(1125,
-                LwM2mResourceInstance.newUnsignedIntegerInstance(0, ULong.valueOf("9223372036854775808")));
-        initialValues.put(1130, LwM2mResourceInstance.newFloatInstance(0, 3.14159d));
-        initialValues.put(1140, LwM2mResourceInstance.newBooleanInstance(0, true));
-        initialValues.put(1150,
-                LwM2mResourceInstance.newBinaryInstance(0, Hex.decodeHex("0123456789ABCDEF".toCharArray())));
-        initialValues.put(1160, LwM2mResourceInstance.newDateInstance(0, new Date(946684800000l)));
-        initialValues.put(1170, LwM2mResourceInstance.newObjectLinkInstance(0, new ObjectLink(3, 0)));
+        initialValues.put(1110, LwM2mResourceInstance.newStringInstance(0, INITIAL_STRING_VALUE));
+        initialValues.put(1120, LwM2mResourceInstance.newIntegerInstance(0, INITIAL_INTEGER_VALUE));
+        initialValues.put(1125, LwM2mResourceInstance.newUnsignedIntegerInstance(0, INITIAL_UNSIGNED_INTEGER_VALUE));
+        initialValues.put(1130, LwM2mResourceInstance.newFloatInstance(0, INITIAL_FLOAT_VALUE));
+        initialValues.put(1140, LwM2mResourceInstance.newBooleanInstance(0, INITIAL_BOOLEAN_VALUE));
+        initialValues.put(1150, LwM2mResourceInstance.newBinaryInstance(0, INITIAL_OPAQUE_VALUE));
+        initialValues.put(1160, LwM2mResourceInstance.newDateInstance(0, INITIAL_TIME_VALUE));
+        initialValues.put(1170, LwM2mResourceInstance.newObjectLinkInstance(0, INITIAL_OBJLINK_VALUE));
     }
 
     private void clearValues() {
diff --git a/leshan-core/src/test/java/org/eclipse/leshan/core/model/ValidateModelsTest.java b/leshan-core/src/test/java/org/eclipse/leshan/core/model/ValidateModelsTest.java
index 7c4d4a36..943bf0f4 100644
--- a/leshan-core/src/test/java/org/eclipse/leshan/core/model/ValidateModelsTest.java
+++ b/leshan-core/src/test/java/org/eclipse/leshan/core/model/ValidateModelsTest.java
@@ -17,6 +17,7 @@ package org.eclipse.leshan.core.model;
 
 import java.io.IOException;
 
+import org.eclipse.leshan.core.util.TestObjectLoader;
 import org.junit.Test;
 
 public class ValidateModelsTest {
@@ -25,4 +26,9 @@ public class ValidateModelsTest {
     public void validate_embedded_models() throws InvalidModelException, InvalidDDFFileException, IOException {
         ObjectLoader.loadDdfResources("/models/", ObjectLoader.ddfpaths, true);
     }
+
+    @Test
+    public void validate_test_models() throws InvalidModelException, InvalidDDFFileException, IOException {
+        ObjectLoader.loadDdfResources("/models/", TestObjectLoader.ddfpaths, true);
+    }
 }
diff --git a/leshan-core/src/test/java/org/eclipse/leshan/core/node/codec/LwM2mNodeDecoderTest.java b/leshan-core/src/test/java/org/eclipse/leshan/core/node/codec/LwM2mNodeDecoderTest.java
index 02ecc3e8..a5545050 100644
--- a/leshan-core/src/test/java/org/eclipse/leshan/core/node/codec/LwM2mNodeDecoderTest.java
+++ b/leshan-core/src/test/java/org/eclipse/leshan/core/node/codec/LwM2mNodeDecoderTest.java
@@ -28,7 +28,6 @@ import java.util.Map;
 
 import org.eclipse.leshan.core.json.LwM2mJsonException;
 import org.eclipse.leshan.core.model.LwM2mModel;
-import org.eclipse.leshan.core.model.ObjectLoader;
 import org.eclipse.leshan.core.model.ObjectModel;
 import org.eclipse.leshan.core.model.ResourceModel;
 import org.eclipse.leshan.core.model.ResourceModel.Operations;
@@ -50,6 +49,7 @@ import org.eclipse.leshan.core.tlv.Tlv;
 import org.eclipse.leshan.core.tlv.Tlv.TlvType;
 import org.eclipse.leshan.core.tlv.TlvEncoder;
 import org.eclipse.leshan.core.util.Hex;
+import org.eclipse.leshan.core.util.TestObjectLoader;
 import org.eclipse.leshan.core.util.datatype.ULong;
 import org.junit.Assert;
 import org.junit.BeforeClass;
@@ -66,7 +66,7 @@ public class LwM2mNodeDecoderTest {
     @BeforeClass
     public static void loadModel() {
         // load default object
-        List<ObjectModel> objects = ObjectLoader.loadDefault();
+        List<ObjectModel> objects = TestObjectLoader.loadAllDefault();
 
         // add object 65 from the LWM2M v1.0.1 specification (figure 28)
         List<ResourceModel> resForObj65 = new ArrayList<>();
diff --git a/leshan-core/src/test/java/org/eclipse/leshan/core/util/TestLwM2mId.java b/leshan-core/src/test/java/org/eclipse/leshan/core/util/TestLwM2mId.java
new file mode 100644
index 00000000..5ae5aca7
--- /dev/null
+++ b/leshan-core/src/test/java/org/eclipse/leshan/core/util/TestLwM2mId.java
@@ -0,0 +1,45 @@
+/*******************************************************************************
+ * Copyright (c) 2022 Sierra Wireless and others.
+ * 
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v2.0
+ * and Eclipse Distribution License v1.0 which accompany this distribution.
+ * 
+ * The Eclipse Public License is available at
+ *    http://www.eclipse.org/legal/epl-v20.html
+ * and the Eclipse Distribution License is available at
+ *    http://www.eclipse.org/org/documents/edl-v10.html.
+ * 
+ * Contributors:
+ *     Sierra Wireless - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.leshan.core.util;
+
+public interface TestLwM2mId {
+    static final int TEST_OBJECT = 3442;
+
+    /* TEST RESOURCES */
+    static final int RESET_VALUES = 0;
+    static final int RANDOMIZE_VALUES = 1;
+    static final int CLEAR_VALUES = 2;
+
+    static final int STRING_VALUE = 110;
+    static final int INTEGER_VALUE = 120;
+    static final int UNSIGNED_INTEGER_VALUE = 125;
+    static final int FLOAT_VALUE = 130;
+    static final int BOOLEAN_VALUE = 140;
+    static final int OPAQUE_VALUE = 150;
+    static final int TIME_VALUE = 160;
+    static final int OBJLINK_VALUE = 170;
+    static final int CORELNK_VALUE = 180;
+
+    static final int MULTIPLE_STRING_VALUE = 1110;
+    static final int MULTIPLE_INTEGER_VALUE = 1120;
+    static final int MULTIPLE_UNSIGNED_INTEGER_VALUE = 1125;
+    static final int MULTIPLE_FLOAT_VALUE = 1130;
+    static final int MULTIPLE_BOOLEAN_VALUE = 1140;
+    static final int MULTIPLE_OPAQUE_VALUE = 1150;
+    static final int MULTIPLE_TIME_VALUE = 1160;
+    static final int MULTIPLE_OBJLINK_VALUE = 1170;
+    static final int MULTIPLE_CORELNK_VALUE = 1180;
+}
diff --git a/leshan-core/src/test/java/org/eclipse/leshan/core/util/TestObjectLoader.java b/leshan-core/src/test/java/org/eclipse/leshan/core/util/TestObjectLoader.java
new file mode 100644
index 00000000..b2270ee8
--- /dev/null
+++ b/leshan-core/src/test/java/org/eclipse/leshan/core/util/TestObjectLoader.java
@@ -0,0 +1,40 @@
+/*******************************************************************************
+ * Copyright (c) 2022 Sierra Wireless and others.
+ * 
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v2.0
+ * and Eclipse Distribution License v1.0 which accompany this distribution.
+ * 
+ * The Eclipse Public License is available at
+ *    http://www.eclipse.org/legal/epl-v20.html
+ * and the Eclipse Distribution License is available at
+ *    http://www.eclipse.org/org/documents/edl-v10.html.
+ * 
+ * Contributors:
+ *     Sierra Wireless - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.leshan.core.util;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.leshan.core.model.ObjectLoader;
+import org.eclipse.leshan.core.model.ObjectModel;
+
+public class TestObjectLoader {
+
+    public static final String[] ddfpaths = new String[] { "3441.xml", "3442.xml" };
+
+    public static List<ObjectModel> loadAllDefault() {
+        List<ObjectModel> result = new ArrayList<>();
+        try {
+            // add default models
+            result.addAll(ObjectLoader.loadAllDefault());
+            // add test models
+            result.addAll(ObjectLoader.loadDdfResources("/models/", ddfpaths));
+        } catch (Exception e) {
+            throw new IllegalStateException("Unable to load models", e);
+        }
+        return result;
+    }
+}
diff --git a/leshan-core/src/test/resources/models/3441.xml b/leshan-core/src/test/resources/models/3441.xml
new file mode 100644
index 00000000..bdd5efca
--- /dev/null
+++ b/leshan-core/src/test/resources/models/3441.xml
@@ -0,0 +1,262 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- BSD-3 Clause License 
+
+    Copyright 2021 Eclipse Foundation.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+3. Neither the name of the copyright holder nor the names of its
+contributors may be used to endorse or promote products derived
+from this software without specific prior written permission.
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+-->
+<LWM2M xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:noNamespaceSchemaLocation="http://openmobilealliance.org/tech/profiles/LWM2M.xsd">
+    <Object ObjectType="MODefinition">
+        <Name>LwM2M v1.0 Test Object</Name>
+        <Description1><![CDATA[This object is for use in interoperability tests of the LwM2M v1.0 technical specification. It contains resources for each available datatype.]]></Description1>
+        <ObjectID>3441</ObjectID>
+        <ObjectURN>urn:oma:lwm2m:ext:3441</ObjectURN>
+        <LWM2MVersion>1.0</LWM2MVersion>
+        <ObjectVersion>1.0</ObjectVersion>
+        <MultipleInstances>Multiple</MultipleInstances>
+        <Mandatory>Optional</Mandatory>
+        <Resources>
+            <Item ID="0">
+                <Name>Reset values</Name>
+                <Operations>E</Operations>
+                <MultipleInstances>Single</MultipleInstances>
+                <Mandatory>Optional</Mandatory>
+                <Type></Type>
+                <RangeEnumeration />
+                <Units />
+                <Description>Reset all resources of this object with their initial value.</Description>
+            </Item>
+            <Item ID="1">
+                <Name>Randomize values</Name>
+                <Operations>E</Operations>
+                <MultipleInstances>Single</MultipleInstances>
+                <Mandatory>Optional</Mandatory>
+                <Type></Type>
+                <RangeEnumeration />
+                <Units />
+                <Description>
+<![CDATA[Set random value to all resources. For multi-instance resources, the number of resource instances is also randomized.
+Randomization should avoid to generate too big payload. We advice to limit value to something like : 
+ - 20 characters for String,
+ - 20 bytes for Opaque,
+ - 10 instances for multi-instance resources. 
+]]>
+                </Description>
+            </Item>
+            <Item ID="2">
+                <Name>Clear values</Name>
+                <Operations>E</Operations>
+                <MultipleInstances>Single</MultipleInstances>
+                <Mandatory>Optional</Mandatory>
+                <Type></Type>
+                <RangeEnumeration />
+                <Units />
+                <Description>
+<![CDATA[Clear all values : 
+ - all multiple resource as empty resource
+ - all number to 0
+ - String to empty string
+ - boolean to false,
+ - opaque to empty byte array,
+ - time to an 1st, 1970 in the UTC time zone
+ - objlink to null link]]>
+                </Description>
+            </Item>
+            <Item ID="3">
+                <Name>Exec With Arguments</Name>
+                <Operations>E</Operations>
+                <MultipleInstances>Single</MultipleInstances>
+                <Mandatory>Optional</Mandatory>
+                <Type></Type>
+                <RangeEnumeration />
+                <Units />
+                <Description>
+<![CDATA[This resources can be used to test "Execute Operation" with Arguments.
+Sent Arguments can be read via "Arguments List"(4) resource.
+E.g. If you send an Exec /3441/0/3 with "3='stringValue',4" as arguments value,
+     then /3441/0/4/3 will be 'stringValue' and /3441/0/4/4 will be an empty string.
+]]>
+                </Description>
+            </Item>
+            <Item ID="4">
+                <Name>Arguments List</Name>
+                <Operations>R</Operations>
+                <MultipleInstances>Multiple</MultipleInstances>
+                <Mandatory>Optional</Mandatory>
+                <Type>String</Type>
+                <RangeEnumeration />
+                <Units />
+                <Description>List of Arguments from last execute on "Exec With Arguments"(3) resource. This resource is not affected by the "Randomize values"(1) executable resource.
+                </Description>
+            </Item>
+            <Item ID="110">
+                <Name>String Value</Name>
+                <Operations>RW</Operations>
+                <MultipleInstances>Single</MultipleInstances>
+                <Mandatory>Optional</Mandatory>
+                <Type>String</Type>
+                <RangeEnumeration />
+                <Units />
+                <Description>Initial value must be "initial value".
+                </Description>
+            </Item>
+            <Item ID="120">
+                <Name>Integer Value</Name>
+                <Operations>RW</Operations>
+                <MultipleInstances>Single</MultipleInstances>
+                <Mandatory>Optional</Mandatory>
+                <Type>Integer</Type>
+                <RangeEnumeration />
+                <Units />
+                <Description>Initial value must be "1024".
+                </Description>
+            </Item>
+            <Item ID="130">
+                <Name>Float Value</Name>
+                <Operations>RW</Operations>
+                <MultipleInstances>Single</MultipleInstances>
+                <Mandatory>Optional</Mandatory>
+                <Type>Float</Type>
+                <RangeEnumeration />
+                <Units />
+                <Description>Initial value must be "3.14159".
+                </Description>
+            </Item>
+            <Item ID="140">
+                <Name>Boolean Value</Name>
+                <Operations>RW</Operations>
+                <MultipleInstances>Single</MultipleInstances>
+                <Mandatory>Optional</Mandatory>
+                <Type>Boolean</Type>
+                <RangeEnumeration />
+                <Units />
+                <Description>Initial value must be "true".</Description>
+            </Item>
+            <Item ID="150">
+                <Name>Opaque Value</Name>
+                <Operations>RW</Operations>
+                <MultipleInstances>Single</MultipleInstances>
+                <Mandatory>Optional</Mandatory>
+                <Type>Opaque</Type>
+                <RangeEnumeration />
+                <Units />
+                <Description>Initial value must be the bytes sequence "0123456789ABCDEF" (Hexadecimal notation).
+                </Description>
+            </Item>
+            <Item ID="160">
+                <Name>Time Value</Name>
+                <Operations>RW</Operations>
+                <MultipleInstances>Single</MultipleInstances>
+                <Mandatory>Optional</Mandatory>
+                <Type>Time</Type>
+                <RangeEnumeration />
+                <Units />
+                <Description>Initial value must be the time to an 1st, 2000 in the UTC time zone. (Timestamp value : 946684800)</Description>
+            </Item>
+            <Item ID="170">
+                <Name>ObjLink Value</Name>
+                <Operations>RW</Operations>
+                <MultipleInstances>Single</MultipleInstances>
+                <Mandatory>Optional</Mandatory>
+                <Type>Objlnk</Type>
+                <RangeEnumeration />
+                <Units />
+                <Description>Initial value must be a link to instance 0 of Device Object 3 (3:0).</Description>
+            </Item>
+            <Item ID="1110">
+                <Name>Multiple String Value</Name>
+                <Operations>RW</Operations>
+                <MultipleInstances>Multiple</MultipleInstances>
+                <Mandatory>Optional</Mandatory>
+                <Type>String</Type>
+                <RangeEnumeration />
+                <Units />
+                <Description>Initial value must be 1 instance with ID 0 and value "initial value".</Description>
+            </Item>
+            <Item ID="1120">
+                <Name>Multiple Integer Value</Name>
+                <Operations>RW</Operations>
+                <MultipleInstances>Multiple</MultipleInstances>
+                <Mandatory>Optional</Mandatory>
+                <Type>Integer</Type>
+                <RangeEnumeration />
+                <Units />
+                <Description>Initial value must be 1 instance with ID 0 and value "1024".</Description>
+            </Item>
+            <Item ID="1130">
+                <Name>Multiple Float Value</Name>
+                <Operations>RW</Operations>
+                <MultipleInstances>Multiple</MultipleInstances>
+                <Mandatory>Optional</Mandatory>
+                <Type>Float</Type>
+                <RangeEnumeration />
+                <Units />
+                <Description>Initial value must be 1 instance with ID 0 and value "3.14159".</Description>
+            </Item>
+            <Item ID="1140">
+                <Name>Multiple Boolean Value</Name>
+                <Operations>RW</Operations>
+                <MultipleInstances>Multiple</MultipleInstances>
+                <Mandatory>Optional</Mandatory>
+                <Type>Boolean</Type>
+                <RangeEnumeration />
+                <Units />
+                <Description>Initial value must be 1 instance with ID 0 and value "true".</Description>
+            </Item>
+            <Item ID="1150">
+                <Name>Multiple Opaque Value</Name>
+                <Operations>RW</Operations>
+                <MultipleInstances>Multiple</MultipleInstances>
+                <Mandatory>Optional</Mandatory>
+                <Type>Opaque</Type>
+                <RangeEnumeration />
+                <Units />
+                <Description>Initial value must be 1 instance with ID 0 and value "0123456789ABCDEF"(Hexadecimal notation of the bytes sequence).</Description>
+            </Item>
+            <Item ID="1160">
+                <Name>Multiple Time Value</Name>
+                <Operations>RW</Operations>
+                <MultipleInstances>Multiple</MultipleInstances>
+                <Mandatory>Optional</Mandatory>
+                <Type>Time</Type>
+                <RangeEnumeration />
+                <Units />
+                <Description>Initial value must be 1 instance with ID 0 and value 1st, 2000 in the UTC time zone (Timestamp value : 946684800).</Description>
+            </Item>
+            <Item ID="1170">
+                <Name>Multiple ObjLink Value</Name>
+                <Operations>RW</Operations>
+                <MultipleInstances>Multiple</MultipleInstances>
+                <Mandatory>Optional</Mandatory>
+                <Type>Objlnk</Type>
+                <RangeEnumeration />
+                <Units />
+                <Description>Initial value must be 1 instance with ID 0 and value "3:0".</Description>
+            </Item>
+        </Resources>
+        <Description2></Description2>
+    </Object>
+</LWM2M>
diff --git a/leshan-core/src/test/resources/models/3442.xml b/leshan-core/src/test/resources/models/3442.xml
new file mode 100644
index 00000000..b4607cea
--- /dev/null
+++ b/leshan-core/src/test/resources/models/3442.xml
@@ -0,0 +1,332 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- BSD-3 Clause License 
+
+    Copyright 2021 Eclipse Foundation.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+3. Neither the name of the copyright holder nor the names of its
+contributors may be used to endorse or promote products derived
+from this software without specific prior written permission.
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+-->
+<LWM2M xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:noNamespaceSchemaLocation="http://openmobilealliance.org/tech/profiles/LWM2M-v1_1.xsd">
+    <Object ObjectType="MODefinition">
+        <Name>LwM2M v1.1 Test Object</Name>
+        <Description1><![CDATA[This object aims to make easier to do interoperability tests about LWM2M v1.1. It contains resources for each available datatype.]]></Description1>
+        <ObjectID>3442</ObjectID>
+        <ObjectURN>urn:oma:lwm2m:ext:3442</ObjectURN>
+        <LWM2MVersion>1.1</LWM2MVersion>
+        <ObjectVersion>1.0</ObjectVersion>
+        <MultipleInstances>Multiple</MultipleInstances>
+        <Mandatory>Optional</Mandatory>
+        <Resources>
+            <Item ID="0">
+                <Name>Reset values</Name>
+                <Operations>E</Operations>
+                <MultipleInstances>Single</MultipleInstances>
+                <Mandatory>Optional</Mandatory>
+                <Type></Type>
+                <RangeEnumeration />
+                <Units />
+                <Description>Reset all resources of this object with their initial value.</Description>
+            </Item>
+            <Item ID="1">
+                <Name>Randomize values</Name>
+                <Operations>E</Operations>
+                <MultipleInstances>Single</MultipleInstances>
+                <Mandatory>Optional</Mandatory>
+                <Type></Type>
+                <RangeEnumeration />
+                <Units />
+                <Description>
+<![CDATA[Set random value to all resources. For multi-instance resources, the number of resource instances is also randomized.
+Randomization should avoid to generate too big payload. We advice to limit value to something like : 
+ - 20 characters for String,
+ - 20 bytes for Opaque,
+ - 10 instances for multi-instance resources. 
+]]>
+                </Description>
+            </Item>
+            <Item ID="2">
+                <Name>Clear values</Name>
+                <Operations>E</Operations>
+                <MultipleInstances>Single</MultipleInstances>
+                <Mandatory>Optional</Mandatory>
+                <Type></Type>
+                <RangeEnumeration />
+                <Units />
+                <Description>
+<![CDATA[Clear all values : 
+ - all multiple resource as empty resource
+ - all number to 0
+ - String to empty string
+ - boolean to false,
+ - opaque to empty byte array,
+ - time to an 1st, 1970 in the UTC time zone
+ - objlink to null link]]>
+                </Description>
+            </Item>
+            <Item ID="3">
+                <Name>Exec With Arguments</Name>
+                <Operations>E</Operations>
+                <MultipleInstances>Single</MultipleInstances>
+                <Mandatory>Optional</Mandatory>
+                <Type></Type>
+                <RangeEnumeration />
+                <Units />
+                <Description>
+<![CDATA[This resources can be used to test "Execute Operation" with Arguments.
+Sent Arguments can be read via "Arguments List"(4) resource.
+E.g. If you send an Exec /3441/0/3 with "3='stringValue',4" as arguments value,
+     then /3441/0/4/3 will be 'stringValue' and /3441/0/4/4 will be an empty string.
+]]>
+                </Description>
+            </Item>
+            <Item ID="4">
+                <Name>Arguments List</Name>
+                <Operations>R</Operations>
+                <MultipleInstances>Multiple</MultipleInstances>
+                <Mandatory>Optional</Mandatory>
+                <Type>String</Type>
+                <RangeEnumeration />
+                <Units />
+                <Description>List of Arguments from last execute on "Exec With Arguments"(3) resource. This resource is not affected by "Randomize values"(1) executable resource.
+                </Description>
+            </Item>
+            <Item ID="5">
+                <Name>Send Data</Name>
+                <Operations>E</Operations>
+                <MultipleInstances>Single</MultipleInstances>
+                <Mandatory>Optional</Mandatory>
+                <Type></Type>
+                <RangeEnumeration />
+                <Units />
+                <Description>
+<![CDATA[This resources can be used to test "Send Operation".
+When LWM2M client received the Execute Request, it must send data for each resources listed in "Resources to Send"(6) resource.
+By default LWM2M client chooses the Content format and Timeout to use for this Send Request, but Arguments can optionally be used to change it.
+Argument 0 must contain the Content Format code. e.g. 0='110' for SENML_JSON.
+Argument 1 must contain the request timeout in ms. e.g. 1='2000' for 2s timeout.
+]]>
+               </Description>
+            </Item>
+            <Item ID="6">
+                <Name>Resources to Send</Name>
+                <Operations>RW</Operations>
+                <MultipleInstances>Multiple</MultipleInstances>
+                <Mandatory>Optional</Mandatory>
+                <Type>String</Type>
+                <RangeEnumeration />
+                <Units />
+                <Description>List of path to LWM2M node (Object / Object Instance / Resource / Resource Instance) used by "Send Data "(5) resource. This resource is not affected by "Randomize values"(1) executable resource.
+                </Description>
+            </Item>
+            <Item ID="110">
+                <Name>String Value</Name>
+                <Operations>RW</Operations>
+                <MultipleInstances>Single</MultipleInstances>
+                <Mandatory>Optional</Mandatory>
+                <Type>String</Type>
+                <RangeEnumeration />
+                <Units />
+                <Description>Initial value must be "initial value".
+                </Description>
+            </Item>
+            <Item ID="120">
+                <Name>Integer Value</Name>
+                <Operations>RW</Operations>
+                <MultipleInstances>Single</MultipleInstances>
+                <Mandatory>Optional</Mandatory>
+                <Type>Integer</Type>
+                <RangeEnumeration />
+                <Units />
+                <Description>Initial value must be "1024".
+                </Description>
+            </Item>
+            <Item ID="125">
+                <Name>Unsigned Integer Value</Name>
+                <Operations>RW</Operations>
+                <MultipleInstances>Single</MultipleInstances>
+                <Mandatory>Optional</Mandatory>
+                <Type>Unsigned Integer</Type>
+                <RangeEnumeration />
+                <Units />
+                <Description>Initial value must be 2^63 (9223372036854775808).
+                </Description>
+            </Item>
+            <Item ID="130">
+                <Name>Float Value</Name>
+                <Operations>RW</Operations>
+                <MultipleInstances>Single</MultipleInstances>
+                <Mandatory>Optional</Mandatory>
+                <Type>Float</Type>
+                <RangeEnumeration />
+                <Units />
+                <Description>Initial value must be "3.14159".
+                </Description>
+            </Item>
+            <Item ID="140">
+                <Name>Boolean Value</Name>
+                <Operations>RW</Operations>
+                <MultipleInstances>Single</MultipleInstances>
+                <Mandatory>Optional</Mandatory>
+                <Type>Boolean</Type>
+                <RangeEnumeration />
+                <Units />
+                <Description>Initial value must be "true".</Description>
+            </Item>
+            <Item ID="150">
+                <Name>Opaque Value</Name>
+                <Operations>RW</Operations>
+                <MultipleInstances>Single</MultipleInstances>
+                <Mandatory>Optional</Mandatory>
+                <Type>Opaque</Type>
+                <RangeEnumeration />
+                <Units />
+                <Description>Initial value must be the bytes sequence "0123456789ABCDEF" (Hexadecimal notation).
+                </Description>
+            </Item>
+            <Item ID="160">
+                <Name>Time Value</Name>
+                <Operations>RW</Operations>
+                <MultipleInstances>Single</MultipleInstances>
+                <Mandatory>Optional</Mandatory>
+                <Type>Time</Type>
+                <RangeEnumeration />
+                <Units />
+                <Description>Initial value must be the time to an 1st, 2000 in the UTC time zone. (Timestamp value : 946684800)</Description>
+            </Item>
+            <Item ID="170">
+                <Name>ObjLink Value</Name>
+                <Operations>RW</Operations>
+                <MultipleInstances>Single</MultipleInstances>
+                <Mandatory>Optional</Mandatory>
+                <Type>Objlnk</Type>
+                <RangeEnumeration />
+                <Units />
+                <Description>Initial value must be a link to instance 0 of Device Object 3 (3:0).</Description>
+            </Item>
+            <Item ID="180">
+                <Name>CoreLnk Value</Name>
+                <Operations>RW</Operations>
+                <MultipleInstances>Single</MultipleInstances>
+                <Mandatory>Optional</Mandatory>
+                <Type>Corelnk</Type>
+                <RangeEnumeration />
+                <Units />
+                <Description><![CDATA[Initial value must be a the "</3442>".]]></Description>
+            </Item>
+            <Item ID="1110">
+                <Name>Multiple String Value</Name>
+                <Operations>RW</Operations>
+                <MultipleInstances>Multiple</MultipleInstances>
+                <Mandatory>Optional</Mandatory>
+                <Type>String</Type>
+                <RangeEnumeration />
+                <Units />
+                <Description>Initial value must be 1 instance with ID 0 and value "initial value".</Description>
+            </Item>
+            <Item ID="1120">
+                <Name>Multiple Integer Value</Name>
+                <Operations>RW</Operations>
+                <MultipleInstances>Multiple</MultipleInstances>
+                <Mandatory>Optional</Mandatory>
+                <Type>Integer</Type>
+                <RangeEnumeration />
+                <Units />
+                <Description>Initial value must be 1 instance with ID 0 and value "1024".</Description>
+            </Item>
+            <Item ID="1125">
+                <Name>Multiple Unsigned Integer Value</Name>
+                <Operations>RW</Operations>
+                <MultipleInstances>Multiple</MultipleInstances>
+                <Mandatory>Optional</Mandatory>
+                <Type>Unsigned Integer</Type>
+                <RangeEnumeration />
+                <Units />
+                <Description>Initial value must be 1 instance with ID 0 and value 2^63 (9223372036854775808).
+                </Description>
+            </Item>
+            <Item ID="1130">
+                <Name>Multiple Float Value</Name>
+                <Operations>RW</Operations>
+                <MultipleInstances>Multiple</MultipleInstances>
+                <Mandatory>Optional</Mandatory>
+                <Type>Float</Type>
+                <RangeEnumeration />
+                <Units />
+                <Description>Initial value must be 1 instance with ID 0 and value "3.14159".</Description>
+            </Item>
+            <Item ID="1140">
+                <Name>Multiple Boolean Value</Name>
+                <Operations>RW</Operations>
+                <MultipleInstances>Multiple</MultipleInstances>
+                <Mandatory>Optional</Mandatory>
+                <Type>Boolean</Type>
+                <RangeEnumeration />
+                <Units />
+                <Description>Initial value must be 1 instance with ID 0 and value "true".</Description>
+            </Item>
+            <Item ID="1150">
+                <Name>Multiple Opaque Value</Name>
+                <Operations>RW</Operations>
+                <MultipleInstances>Multiple</MultipleInstances>
+                <Mandatory>Optional</Mandatory>
+                <Type>Opaque</Type>
+                <RangeEnumeration />
+                <Units />
+                <Description>Initial value must be 1 instance with ID 0 and value "0123456789ABCDEF"(Hexadecimal notation of the bytes sequence).</Description>
+            </Item>
+            <Item ID="1160">
+                <Name>Multiple Time Value</Name>
+                <Operations>RW</Operations>
+                <MultipleInstances>Multiple</MultipleInstances>
+                <Mandatory>Optional</Mandatory>
+                <Type>Time</Type>
+                <RangeEnumeration />
+                <Units />
+                <Description>Initial value must be 1 instance with ID 0 and value 1st, 2000 in the UTC time zone (Timestamp value : 946684800).</Description>
+            </Item>
+            <Item ID="1170">
+                <Name>Multiple ObjLink Value</Name>
+                <Operations>RW</Operations>
+                <MultipleInstances>Multiple</MultipleInstances>
+                <Mandatory>Optional</Mandatory>
+                <Type>Objlnk</Type>
+                <RangeEnumeration />
+                <Units />
+                <Description>Initial value must be 1 instance with ID 0 and value "3:0".</Description>
+            </Item>
+             <Item ID="1180">
+                <Name>Multiple CoreLnk Value</Name>
+                <Operations>RW</Operations>
+                <MultipleInstances>Multiple</MultipleInstances>
+                <Mandatory>Optional</Mandatory>
+                <Type>Corelnk</Type>
+                <RangeEnumeration />
+                <Units />
+                <Description><![CDATA[Initial value must be 1 instance with ID 0 and value "</3442>".]]></Description>
+            </Item>
+        </Resources>
+        <Description2></Description2>
+    </Object>
+</LWM2M>
diff --git a/leshan-integration-tests/pom.xml b/leshan-integration-tests/pom.xml
index 4878b914..bb8ff089 100644
--- a/leshan-integration-tests/pom.xml
+++ b/leshan-integration-tests/pom.xml
@@ -27,6 +27,12 @@ Contributors:
 
 
     <dependencies>
+        <dependency>
+            <groupId>org.eclipse.leshan</groupId>
+            <artifactId>leshan-core</artifactId>
+            <type>test-jar</type>
+            <version>${project.version}</version>
+        </dependency>
         <dependency>
             <groupId>org.eclipse.leshan</groupId>
             <artifactId>leshan-server-cf</artifactId>
diff --git a/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/QueueModeTest.java b/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/QueueModeTest.java
index 684a0756..d58d4413 100644
--- a/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/QueueModeTest.java
+++ b/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/QueueModeTest.java
@@ -63,7 +63,7 @@ public class QueueModeTest {
         // Check client is well registered
         queueModeHelper.assertClientRegisterered();
         assertArrayEquals(linkParser.parseCoreLinkFormat(
-                "</>;rt=\"oma.lwm2m\";ct=\"60 110 112 11542 11543\",</1>;ver=1.1,</1/0>,</2>,</3>;ver=1.1,</3/0>,</2000/0>"
+                "</>;rt=\"oma.lwm2m\";ct=\"60 110 112 11542 11543\",</1>;ver=1.1,</1/0>,</2>,</3>;ver=1.1,</3/0>,</3442/0>"
                         .getBytes()),
                 queueModeHelper.getCurrentRegistration().getObjectLinks());
 
diff --git a/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/RegistrationTest.java b/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/RegistrationTest.java
index a355b06c..d5f01ab4 100644
--- a/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/RegistrationTest.java
+++ b/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/RegistrationTest.java
@@ -91,7 +91,7 @@ public class RegistrationTest {
         // Check client is well registered
         helper.assertClientRegisterered();
         assertArrayEquals(linkParser.parseCoreLinkFormat(
-                "</>;rt=\"oma.lwm2m\";ct=\"60 110 112 1542 1543 11542 11543\",</1>;ver=1.1,</1/0>,</2>,</3>;ver=1.1,</3/0>,</2000/0>,</2000/1>"
+                "</>;rt=\"oma.lwm2m\";ct=\"60 110 112 1542 1543 11542 11543\",</1>;ver=1.1,</1/0>,</2>,</3>;ver=1.1,</3/0>,</3442/0>"
                         .getBytes()),
                 helper.getCurrentRegistration().getObjectLinks());
 
@@ -117,7 +117,7 @@ public class RegistrationTest {
         // Check client is well registered
         helper.assertClientRegisterered();
         assertArrayEquals(linkParser.parseCoreLinkFormat(
-                "</>;rt=\"oma.lwm2m\";ct=\"60 110 112 1542 1543 11542 11543\",</1>;ver=1.1,</1/0>,</2>,</3>;ver=1.1,</3/0>,</2000/0>,</2000/1>"
+                "</>;rt=\"oma.lwm2m\";ct=\"60 110 112 1542 1543 11542 11543\",</1>;ver=1.1,</1/0>,</2>,</3>;ver=1.1,</3/0>,</3442/0>"
                         .getBytes()),
                 helper.getCurrentRegistration().getObjectLinks());
 
@@ -268,7 +268,7 @@ public class RegistrationTest {
         assertNotNull(helper.getLastRegistration());
         assertEquals(additionalAttributes, helper.getLastRegistration().getAdditionalRegistrationAttributes());
         assertArrayEquals(linkParser.parseCoreLinkFormat(
-                "</>;rt=\"oma.lwm2m\";ct=\"60 110 112 1542 1543 11542 11543\",</1>;ver=1.1,</1/0>,</2>,</3>;ver=1.1,</3/0>,</2000/0>,</2000/1>"
+                "</>;rt=\"oma.lwm2m\";ct=\"60 110 112 1542 1543 11542 11543\",</1>;ver=1.1,</1/0>,</2>,</3>;ver=1.1,</3/0>,</3442/0>"
                         .getBytes()),
                 helper.getCurrentRegistration().getObjectLinks());
     }
diff --git a/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/create/CreateTest.java b/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/create/CreateTest.java
index 7a5668e1..485c453d 100644
--- a/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/create/CreateTest.java
+++ b/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/create/CreateTest.java
@@ -18,7 +18,6 @@
 
 package org.eclipse.leshan.integration.tests.create;
 
-import static org.eclipse.leshan.integration.tests.util.IntegrationTestHelper.*;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.core.Is.is;
 import static org.hamcrest.core.IsInstanceOf.instanceOf;
@@ -28,6 +27,7 @@ import java.util.Arrays;
 import java.util.Collection;
 
 import org.eclipse.californium.core.coap.Response;
+import org.eclipse.leshan.core.LwM2mId;
 import org.eclipse.leshan.core.ResponseCode;
 import org.eclipse.leshan.core.node.LwM2mObject;
 import org.eclipse.leshan.core.node.LwM2mObjectInstance;
@@ -54,10 +54,10 @@ public class CreateTest {
     @Parameters(name = "{0}")
     public static Collection<?> contentFormats() {
         return Arrays.asList(new Object[][] { //
-                                { ContentFormat.TLV }, //
-                                { ContentFormat.JSON }, //
-                                { ContentFormat.SENML_JSON }, //
-                                { ContentFormat.SENML_CBOR } });
+                { ContentFormat.TLV }, //
+                { ContentFormat.JSON }, //
+                { ContentFormat.SENML_JSON }, //
+                { ContentFormat.SENML_CBOR } });
     }
 
     private ContentFormat contentFormat;
@@ -163,31 +163,32 @@ public class CreateTest {
     @Test
     public void cannot_create_instance_without_all_required_resources() throws InterruptedException {
         // create ACL instance without any resources
-        CreateResponse response = helper.server.send(helper.getCurrentRegistration(),
-                new CreateRequest(contentFormat, TEST_OBJECT_ID, new LwM2mObjectInstance(0, new LwM2mResource[0])));
+        CreateResponse response = helper.server.send(helper.getCurrentRegistration(), new CreateRequest(contentFormat,
+                LwM2mId.ACCESS_CONTROL, new LwM2mObjectInstance(0, new LwM2mResource[0])));
 
         // verify result
         assertEquals(ResponseCode.BAD_REQUEST, response.getCode());
         assertNotNull(response.getCoapResponse());
         assertThat(response.getCoapResponse(), is(instanceOf(Response.class)));
 
-        // create ACL instance with only 1 mandatory resources (1 missing)
-        CreateResponse response2 = helper.server.send(helper.getCurrentRegistration(), new CreateRequest(contentFormat,
-                TEST_OBJECT_ID,
-                new LwM2mObjectInstance(0, LwM2mSingleResource.newIntegerResource(INTEGER_MANDATORY_RESOURCE_ID, 12))));
+        // create ACL instance with only 1 mandatory resources (2 missing)
+        CreateResponse response2 = helper.server.send(helper.getCurrentRegistration(),
+                new CreateRequest(contentFormat, LwM2mId.ACCESS_CONTROL,
+                        new LwM2mObjectInstance(0, LwM2mSingleResource.newIntegerResource(LwM2mId.ACL_OBJECT_ID, 12))));
 
         // verify result
         assertEquals(ResponseCode.BAD_REQUEST, response2.getCode());
 
-        // create ACL instance
+        // create 2 ACL instances, one with missing mandatory resource and the other with all mandatory resources
         LwM2mObjectInstance instance0 = new LwM2mObjectInstance(0,
-                LwM2mSingleResource.newIntegerResource(INTEGER_MANDATORY_RESOURCE_ID, 22),
-                LwM2mSingleResource.newStringResource(STRING_MANDATORY_RESOURCE_ID, "string"));
+                LwM2mSingleResource.newIntegerResource(LwM2mId.ACL_OBJECT_ID, 22),
+                LwM2mSingleResource.newIntegerResource(LwM2mId.ACL_OBJECT_INSTANCE_ID, 22),
+                LwM2mSingleResource.newIntegerResource(LwM2mId.ACL_ACCESS_CONTROL_OWNER, 11));
         LwM2mObjectInstance instance1 = new LwM2mObjectInstance(1,
-                LwM2mSingleResource.newIntegerResource(INTEGER_MANDATORY_RESOURCE_ID, 22));
+                LwM2mSingleResource.newIntegerResource(LwM2mId.ACL_OBJECT_ID, 22));
 
         CreateResponse response3 = helper.server.send(helper.getCurrentRegistration(),
-                new CreateRequest(contentFormat, TEST_OBJECT_ID, instance0, instance1));
+                new CreateRequest(contentFormat, LwM2mId.ACCESS_CONTROL, instance0, instance1));
 
         // verify result
         assertEquals(ResponseCode.BAD_REQUEST, response3.getCode());
diff --git a/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/observe/ObserveTest.java b/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/observe/ObserveTest.java
index c045f315..af53c216 100644
--- a/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/observe/ObserveTest.java
+++ b/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/observe/ObserveTest.java
@@ -19,7 +19,6 @@
 
 package org.eclipse.leshan.integration.tests.observe;
 
-import static org.eclipse.leshan.integration.tests.util.IntegrationTestHelper.*;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.core.Is.is;
 import static org.hamcrest.core.IsInstanceOf.instanceOf;
@@ -49,6 +48,7 @@ import org.eclipse.leshan.core.response.CancelObservationResponse;
 import org.eclipse.leshan.core.response.LwM2mResponse;
 import org.eclipse.leshan.core.response.ObserveResponse;
 import org.eclipse.leshan.core.response.ReadResponse;
+import org.eclipse.leshan.core.util.TestLwM2mId;
 import org.eclipse.leshan.integration.tests.util.IntegrationTestHelper;
 import org.junit.After;
 import org.junit.Before;
@@ -116,7 +116,7 @@ public class ObserveTest {
         helper.server.getObservationService().addListener(listener);
 
         // multi instance string
-        String expectedPath = "/" + TEST_OBJECT_ID + "/0/" + STRING_RESOURCE_INSTANCE_ID + "/0";
+        String expectedPath = "/" + TestLwM2mId.TEST_OBJECT + "/0/" + TestLwM2mId.MULTIPLE_STRING_VALUE + "/0";
         ObserveResponse observeResponse = helper.server.send(helper.getCurrentRegistration(),
                 new ObserveRequest(expectedPath));
         assertEquals(ResponseCode.CONTENT, observeResponse.getCode());
@@ -152,7 +152,7 @@ public class ObserveTest {
         helper.server.getObservationService().addListener(listener);
 
         // multi instance string
-        String expectedPath = "/" + TEST_OBJECT_ID + "/0/" + STRING_RESOURCE_INSTANCE_ID + "/0";
+        String expectedPath = "/" + TestLwM2mId.TEST_OBJECT + "/0/" + TestLwM2mId.MULTIPLE_STRING_VALUE + "/0";
         ObserveResponse observeResponse = helper.server.send(helper.getCurrentRegistration(),
                 new ObserveRequest(expectedPath));
         assertEquals(ResponseCode.CONTENT, observeResponse.getCode());
@@ -202,7 +202,7 @@ public class ObserveTest {
         helper.server.getObservationService().addListener(listener);
 
         // multi instance string
-        String expectedPath = "/" + TEST_OBJECT_ID + "/0/" + STRING_RESOURCE_INSTANCE_ID + "/0";
+        String expectedPath = "/" + TestLwM2mId.TEST_OBJECT + "/0/" + TestLwM2mId.MULTIPLE_STRING_VALUE + "/0";
         ObserveResponse observeResponse = helper.server.send(helper.getCurrentRegistration(),
                 new ObserveRequest(expectedPath));
         assertEquals(ResponseCode.CONTENT, observeResponse.getCode());
diff --git a/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/read/ReadCompositeTest.java b/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/read/ReadCompositeTest.java
index 2778a9d5..02bd2244 100644
--- a/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/read/ReadCompositeTest.java
+++ b/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/read/ReadCompositeTest.java
@@ -29,6 +29,7 @@ import org.eclipse.leshan.core.node.LwM2mSingleResource;
 import org.eclipse.leshan.core.request.ContentFormat;
 import org.eclipse.leshan.core.request.ReadCompositeRequest;
 import org.eclipse.leshan.core.response.ReadCompositeResponse;
+import org.eclipse.leshan.core.util.TestLwM2mId;
 import org.eclipse.leshan.integration.tests.util.IntegrationTestHelper;
 import org.junit.After;
 import org.junit.Before;
@@ -44,9 +45,9 @@ public class ReadCompositeTest {
     @Parameters(name = "{0}{1}")
     public static Collection<?> contentFormats() {
         return Arrays.asList(new Object[][] { //
-                                // {request content format, response content format}
-                                { ContentFormat.SENML_JSON, ContentFormat.SENML_JSON }, //
-                                { ContentFormat.SENML_CBOR, ContentFormat.SENML_CBOR } });
+                // {request content format, response content format}
+                { ContentFormat.SENML_JSON, ContentFormat.SENML_JSON }, //
+                { ContentFormat.SENML_CBOR, ContentFormat.SENML_CBOR } });
     }
 
     private ContentFormat requestContentFormat;
@@ -96,16 +97,17 @@ public class ReadCompositeTest {
 
     @Test
     public void can_read_resource_instance() throws InterruptedException {
-        // read device model number
+        // read resource instance
+        String path = "/" + TestLwM2mId.TEST_OBJECT + "/0/" + TestLwM2mId.MULTIPLE_STRING_VALUE + "/0";
         ReadCompositeResponse response = helper.server.send(helper.getCurrentRegistration(),
-                new ReadCompositeRequest(requestContentFormat, responseContentFormat, "/2000/0/65010/1"));
+                new ReadCompositeRequest(requestContentFormat, responseContentFormat, path));
 
         // verify result
         assertEquals(CONTENT, response.getCode());
         assertContentFormat(responseContentFormat, response);
 
-        LwM2mResourceInstance resource = (LwM2mResourceInstance) response.getContent("/2000/0/65010/1");
-        assertEquals(1, resource.getId());
+        LwM2mResourceInstance resource = (LwM2mResourceInstance) response.getContent(path);
+        assertEquals(0, resource.getId());
         assertEquals(Type.STRING, resource.getType());
 
     }
diff --git a/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/read/ReadOpaqueValueTest.java b/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/read/ReadOpaqueValueTest.java
index 0c2396fa..1921d16b 100644
--- a/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/read/ReadOpaqueValueTest.java
+++ b/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/read/ReadOpaqueValueTest.java
@@ -16,9 +16,8 @@
 package org.eclipse.leshan.integration.tests.read;
 
 import static org.eclipse.leshan.core.ResponseCode.CONTENT;
-import static org.eclipse.leshan.integration.tests.util.IntegrationTestHelper.*;
 import static org.eclipse.leshan.integration.tests.util.TestUtil.assertContentFormat;
-import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.*;
 
 import java.util.Arrays;
 import java.util.Collection;
@@ -26,8 +25,11 @@ import java.util.Collection;
 import org.eclipse.leshan.core.model.ResourceModel.Type;
 import org.eclipse.leshan.core.node.LwM2mResource;
 import org.eclipse.leshan.core.request.ContentFormat;
+import org.eclipse.leshan.core.request.ExecuteRequest;
 import org.eclipse.leshan.core.request.ReadRequest;
+import org.eclipse.leshan.core.response.ExecuteResponse;
 import org.eclipse.leshan.core.response.ReadResponse;
+import org.eclipse.leshan.core.util.TestLwM2mId;
 import org.eclipse.leshan.integration.tests.util.IntegrationTestHelper;
 import org.junit.After;
 import org.junit.Before;
@@ -43,13 +45,13 @@ public class ReadOpaqueValueTest {
     @Parameters(name = "{0}")
     public static Collection<?> contentFormats() {
         return Arrays.asList(new Object[][] { //
-                                { ContentFormat.OPAQUE }, //
-                                { ContentFormat.TEXT }, //
-                                { ContentFormat.TLV }, //
-                                { ContentFormat.CBOR }, //
-                                { ContentFormat.JSON }, //
-                                { ContentFormat.SENML_JSON }, //
-                                { ContentFormat.SENML_CBOR } });
+                { ContentFormat.OPAQUE }, //
+                { ContentFormat.TEXT }, //
+                { ContentFormat.TLV }, //
+                { ContentFormat.CBOR }, //
+                { ContentFormat.JSON }, //
+                { ContentFormat.SENML_JSON }, //
+                { ContentFormat.SENML_CBOR } });
     }
 
     private ContentFormat contentFormat;
@@ -77,9 +79,14 @@ public class ReadOpaqueValueTest {
 
     @Test
     public void can_read_empty_opaque_resource() throws InterruptedException {
+        // clear value of TEST object
+        ExecuteResponse clearResponse = helper.server.send(helper.getCurrentRegistration(),
+                new ExecuteRequest(TestLwM2mId.TEST_OBJECT, 0, TestLwM2mId.CLEAR_VALUES));
+        assertTrue(clearResponse.isSuccess());
+
         // read device model number
         ReadResponse response = helper.server.send(helper.getCurrentRegistration(),
-                new ReadRequest(contentFormat, TEST_OBJECT_ID, 1, OPAQUE_RESOURCE_ID));
+                new ReadRequest(contentFormat, TestLwM2mId.TEST_OBJECT, 0, TestLwM2mId.OPAQUE_VALUE));
 
         // verify result
         assertEquals(CONTENT, response.getCode());
diff --git a/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/read/ReadSingleValueTest.java b/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/read/ReadSingleValueTest.java
index 778f28e1..4ce13b6c 100644
--- a/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/read/ReadSingleValueTest.java
+++ b/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/read/ReadSingleValueTest.java
@@ -16,18 +16,19 @@
 package org.eclipse.leshan.integration.tests.read;
 
 import static org.eclipse.leshan.core.ResponseCode.*;
-import static org.eclipse.leshan.integration.tests.util.IntegrationTestHelper.*;
 import static org.eclipse.leshan.integration.tests.util.TestUtil.assertContentFormat;
 import static org.junit.Assert.assertEquals;
 
 import java.util.Arrays;
 import java.util.Collection;
 
+import org.eclipse.leshan.client.object.LwM2mTestObject;
 import org.eclipse.leshan.core.node.LwM2mResource;
 import org.eclipse.leshan.core.node.LwM2mResourceInstance;
 import org.eclipse.leshan.core.request.ContentFormat;
 import org.eclipse.leshan.core.request.ReadRequest;
 import org.eclipse.leshan.core.response.ReadResponse;
+import org.eclipse.leshan.core.util.TestLwM2mId;
 import org.eclipse.leshan.integration.tests.util.IntegrationTestHelper;
 import org.junit.After;
 import org.junit.Before;
@@ -43,12 +44,12 @@ public class ReadSingleValueTest {
     @Parameters(name = "{0}")
     public static Collection<?> contentFormats() {
         return Arrays.asList(new Object[][] { //
-                                { ContentFormat.TEXT }, //
-                                { ContentFormat.TLV }, //
-                                { ContentFormat.CBOR }, //
-                                { ContentFormat.JSON }, //
-                                { ContentFormat.SENML_JSON }, //
-                                { ContentFormat.SENML_CBOR } });
+                { ContentFormat.TEXT }, //
+                { ContentFormat.TLV }, //
+                { ContentFormat.CBOR }, //
+                { ContentFormat.JSON }, //
+                { ContentFormat.SENML_JSON }, //
+                { ContentFormat.SENML_CBOR } });
     }
 
     private ContentFormat contentFormat;
@@ -92,9 +93,9 @@ public class ReadSingleValueTest {
 
     @Test
     public void can_read_resource_instance() throws InterruptedException {
-        // read device model number
+        // read resource instance
         ReadResponse response = helper.server.send(helper.getCurrentRegistration(),
-                new ReadRequest(contentFormat, TEST_OBJECT_ID, 0, STRING_RESOURCE_INSTANCE_ID, 0));
+                new ReadRequest(contentFormat, TestLwM2mId.TEST_OBJECT, 0, TestLwM2mId.MULTIPLE_STRING_VALUE, 0));
 
         // verify result
         assertEquals(CONTENT, response.getCode());
@@ -102,14 +103,14 @@ public class ReadSingleValueTest {
 
         LwM2mResourceInstance resourceInstance = (LwM2mResourceInstance) response.getContent();
         assertEquals(0, resourceInstance.getId());
-        assertEquals(MULTI_INSTANCE, resourceInstance.getValue());
+        assertEquals(LwM2mTestObject.INITIAL_STRING_VALUE, resourceInstance.getValue());
     }
 
     @Test
     public void cannot_read_non_multiple_resource_instance() throws InterruptedException {
-        // read device model number
+        // read single instance resource
         ReadResponse response = helper.server.send(helper.getCurrentRegistration(),
-                new ReadRequest(contentFormat, TEST_OBJECT_ID, 0, INTEGER_RESOURCE_ID, 0));
+                new ReadRequest(contentFormat, TestLwM2mId.TEST_OBJECT, 0, TestLwM2mId.INTEGER_VALUE, 0));
 
         // verify result
         assertEquals(BAD_REQUEST, response.getCode());
diff --git a/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/send/SendTimestampedTest.java b/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/send/SendTimestampedTest.java
index 6af5b849..94b0e55a 100644
--- a/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/send/SendTimestampedTest.java
+++ b/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/send/SendTimestampedTest.java
@@ -26,12 +26,12 @@ import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
 
 import org.eclipse.leshan.client.californium.LeshanClientBuilder;
+import org.eclipse.leshan.client.object.LwM2mTestObject;
 import org.eclipse.leshan.client.object.Security;
 import org.eclipse.leshan.client.object.Server;
 import org.eclipse.leshan.client.resource.DummyInstanceEnabler;
 import org.eclipse.leshan.client.resource.LwM2mObjectEnabler;
 import org.eclipse.leshan.client.resource.ObjectsInitializer;
-import org.eclipse.leshan.client.resource.SimpleInstanceEnabler;
 import org.eclipse.leshan.client.servers.ServerIdentity;
 import org.eclipse.leshan.core.LwM2mId;
 import org.eclipse.leshan.core.model.LwM2mModel;
@@ -43,6 +43,7 @@ import org.eclipse.leshan.core.node.TimestampedLwM2mNodes;
 import org.eclipse.leshan.core.node.codec.DefaultLwM2mDecoder;
 import org.eclipse.leshan.core.node.codec.DefaultLwM2mEncoder;
 import org.eclipse.leshan.core.request.ContentFormat;
+import org.eclipse.leshan.core.util.TestLwM2mId;
 import org.eclipse.leshan.integration.tests.util.IntegrationTestHelper;
 import org.eclipse.leshan.integration.tests.util.SynchronousSendListener;
 import org.junit.After;
@@ -101,13 +102,13 @@ public class SendTimestampedTest {
     }
 
     private static LwM2mPath getExamplePath() {
-        return new LwM2mPath("/2000/1/3");
+        return new LwM2mPath(TestLwM2mId.TEST_OBJECT, 0, TestLwM2mId.FLOAT_VALUE);
     }
 
     private static Map<Long, LwM2mNode> getExampleTimestampedNodes() {
         Map<Long, LwM2mNode> timestampedNodes = new HashMap<>();
-        timestampedNodes.put(268435456L, LwM2mSingleResource.newFloatResource(3, 12345));
-        timestampedNodes.put(268435457L, LwM2mSingleResource.newFloatResource(3, 67890));
+        timestampedNodes.put(268435456L, LwM2mSingleResource.newFloatResource(130, 12345));
+        timestampedNodes.put(268435457L, LwM2mSingleResource.newFloatResource(130, 67890));
         return timestampedNodes;
     }
 
@@ -127,8 +128,7 @@ public class SendTimestampedTest {
             initializer.setInstancesForObject(LwM2mId.SERVER, new Server(12345, LIFETIME));
             initializer.setInstancesForObject(LwM2mId.DEVICE, new TestDevice("Eclipse Leshan", MODEL_NUMBER, "12345"));
             initializer.setClassForObject(LwM2mId.ACCESS_CONTROL, DummyInstanceEnabler.class);
-            initializer.setInstancesForObject(TEST_OBJECT_ID, new DummyInstanceEnabler(0),
-                    new SimpleInstanceEnabler(1, FLOAT_RESOURCE_ID, 12345d));
+            initializer.setInstancesForObject(TestLwM2mId.TEST_OBJECT, new LwM2mTestObject());
             List<LwM2mObjectEnabler> objects = initializer.createAll();
 
             // Build Client
@@ -150,8 +150,8 @@ public class SendTimestampedTest {
         @Override
         public byte[] encodeTimestampedNodes(TimestampedLwM2mNodes timestampedNodes, ContentFormat format,
                 LwM2mModel model) {
-            return ("[{\"bn\":\"/2000/1/\",\"n\":\"3\",\"v\":12345,\"t\":268435456},"
-                    + "{\"n\":\"3\",\"v\":67890,\"t\":268435457}]").getBytes(StandardCharsets.UTF_8);
+            return ("[{\"bn\":\"/3442/0/\",\"n\":\"130\",\"v\":12345,\"t\":268435456},"
+                    + "{\"n\":\"130\",\"v\":67890,\"t\":268435457}]").getBytes(StandardCharsets.UTF_8);
         }
     }
 }
diff --git a/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/util/IntegrationTestHelper.java b/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/util/IntegrationTestHelper.java
index a663d680..80ec2151 100644
--- a/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/util/IntegrationTestHelper.java
+++ b/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/util/IntegrationTestHelper.java
@@ -35,12 +35,12 @@ import org.eclipse.californium.elements.Connector;
 import org.eclipse.leshan.client.californium.LeshanClient;
 import org.eclipse.leshan.client.californium.LeshanClientBuilder;
 import org.eclipse.leshan.client.object.Device;
+import org.eclipse.leshan.client.object.LwM2mTestObject;
 import org.eclipse.leshan.client.object.Security;
 import org.eclipse.leshan.client.object.Server;
 import org.eclipse.leshan.client.resource.DummyInstanceEnabler;
 import org.eclipse.leshan.client.resource.LwM2mObjectEnabler;
 import org.eclipse.leshan.client.resource.ObjectsInitializer;
-import org.eclipse.leshan.client.resource.SimpleInstanceEnabler;
 import org.eclipse.leshan.client.servers.ServerIdentity;
 import org.eclipse.leshan.core.LwM2mId;
 import org.eclipse.leshan.core.link.DefaultLinkSerializer;
@@ -48,9 +48,6 @@ import org.eclipse.leshan.core.link.LinkParser;
 import org.eclipse.leshan.core.link.LinkSerializer;
 import org.eclipse.leshan.core.link.lwm2m.DefaultLwM2mLinkParser;
 import org.eclipse.leshan.core.model.ObjectModel;
-import org.eclipse.leshan.core.model.ResourceModel;
-import org.eclipse.leshan.core.model.ResourceModel.Operations;
-import org.eclipse.leshan.core.model.ResourceModel.Type;
 import org.eclipse.leshan.core.model.StaticModel;
 import org.eclipse.leshan.core.node.codec.DefaultLwM2mDecoder;
 import org.eclipse.leshan.core.node.codec.DefaultLwM2mEncoder;
@@ -58,6 +55,7 @@ import org.eclipse.leshan.core.request.Identity;
 import org.eclipse.leshan.core.request.UplinkRequest;
 import org.eclipse.leshan.core.request.argument.Arguments;
 import org.eclipse.leshan.core.response.ExecuteResponse;
+import org.eclipse.leshan.core.util.TestLwM2mId;
 import org.eclipse.leshan.server.californium.LeshanServer;
 import org.eclipse.leshan.server.californium.LeshanServerBuilder;
 import org.eclipse.leshan.server.model.VersionedModelProvider;
@@ -77,26 +75,9 @@ public class IntegrationTestHelper {
     public static final String MODEL_NUMBER = "IT-TEST-123";
     public static final long LIFETIME = 2;
 
-    public static final int TEST_OBJECT_ID = 2000;
-    public static final int STRING_RESOURCE_ID = 0;
-    public static final int BOOLEAN_RESOURCE_ID = 1;
-    public static final int INTEGER_RESOURCE_ID = 2;
-    public static final int FLOAT_RESOURCE_ID = 3;
-    public static final int TIME_RESOURCE_ID = 4;
-    public static final int OPAQUE_RESOURCE_ID = 5;
-    public static final int OBJLNK_MULTI_INSTANCE_RESOURCE_ID = 6;
-    public static final int OBJLNK_SINGLE_INSTANCE_RESOURCE_ID = 7;
-    public static final int INTEGER_MANDATORY_RESOURCE_ID = 8;
-    public static final int STRING_MANDATORY_RESOURCE_ID = 9;
-    public static final int STRING_RESOURCE_INSTANCE_ID = 65010;
-    public static final int UNSIGNED_INTEGER_RESOURCE_ID = 11;
-    public static final int OPAQUE_MULTI_INSTANCE_RESOURCE_ID = 12;
-
     public static final LinkParser linkParser = new DefaultLwM2mLinkParser();
     public static final LinkSerializer linkSerializer = new DefaultLinkSerializer();
 
-    public static final String MULTI_INSTANCE = "multiinstance";
-
     public LeshanServer server;
     public LeshanClient client;
     public AtomicReference<String> currentEndpointIdentifier = new AtomicReference<String>();
@@ -113,40 +94,6 @@ public class IntegrationTestHelper {
         // load default object from the spec
         List<ObjectModel> objectModels = TestObjectLoader.loadDefaultObject();
 
-        // define custom model for testing purpose
-        ResourceModel stringfield = new ResourceModel(STRING_RESOURCE_ID, "stringres", Operations.RW, false, false,
-                Type.STRING, null, null, null);
-        ResourceModel booleanfield = new ResourceModel(BOOLEAN_RESOURCE_ID, "booleanres", Operations.RW, false, false,
-                Type.BOOLEAN, null, null, null);
-        ResourceModel integerfield = new ResourceModel(INTEGER_RESOURCE_ID, "integerres", Operations.RW, false, false,
-                Type.INTEGER, null, null, null);
-        ResourceModel floatfield = new ResourceModel(FLOAT_RESOURCE_ID, "floatres", Operations.RW, false, false,
-                Type.FLOAT, null, null, null);
-        ResourceModel timefield = new ResourceModel(TIME_RESOURCE_ID, "timeres", Operations.RW, false, false, Type.TIME,
-                null, null, null);
-        ResourceModel opaquefield = new ResourceModel(OPAQUE_RESOURCE_ID, "opaque", Operations.RW, false, false,
-                Type.OPAQUE, null, null, null);
-        ResourceModel objlnkfield = new ResourceModel(OBJLNK_MULTI_INSTANCE_RESOURCE_ID, "objlnk", Operations.RW, true,
-                false, Type.OBJLNK, null, null, null);
-        ResourceModel objlnkSinglefield = new ResourceModel(OBJLNK_SINGLE_INSTANCE_RESOURCE_ID, "objlnk", Operations.RW,
-                false, false, Type.OBJLNK, null, null, null);
-        ResourceModel integermandatoryfield = new ResourceModel(INTEGER_MANDATORY_RESOURCE_ID, "integermandatory",
-                Operations.RW, false, true, Type.INTEGER, null, null, null);
-        ResourceModel stringmandatoryfield = new ResourceModel(STRING_MANDATORY_RESOURCE_ID, "stringmandatory",
-                Operations.RW, false, true, Type.STRING, null, null, null);
-        ResourceModel multiInstance = new ResourceModel(STRING_RESOURCE_INSTANCE_ID, MULTI_INSTANCE, Operations.RW,
-                true, false, Type.STRING, null, null, null);
-        ResourceModel unsignedintegerfield = new ResourceModel(UNSIGNED_INTEGER_RESOURCE_ID, "unsigned", Operations.RW,
-                false, false, Type.UNSIGNED_INTEGER, null, null, null);
-
-        ResourceModel opaqueMultiField = new ResourceModel(OPAQUE_MULTI_INSTANCE_RESOURCE_ID, "opaque_multi",
-                Operations.RW, true, false, Type.OPAQUE, null, null, null);
-
-        objectModels.add(new ObjectModel(TEST_OBJECT_ID, "testobject", null, ObjectModel.DEFAULT_VERSION, true, false,
-                stringfield, booleanfield, integerfield, floatfield, timefield, opaquefield, objlnkfield,
-                objlnkSinglefield, integermandatoryfield, stringmandatoryfield, multiInstance, unsignedintegerfield,
-                opaqueMultiField));
-
         return objectModels;
     }
 
@@ -199,8 +146,7 @@ public class IntegrationTestHelper {
         initializer.setInstancesForObject(LwM2mId.SERVER, new Server(12345, LIFETIME));
         initializer.setInstancesForObject(LwM2mId.DEVICE, new TestDevice("Eclipse Leshan", MODEL_NUMBER, "12345"));
         initializer.setClassForObject(LwM2mId.ACCESS_CONTROL, DummyInstanceEnabler.class);
-        initializer.setInstancesForObject(TEST_OBJECT_ID, new DummyInstanceEnabler(0),
-                new SimpleInstanceEnabler(1, OPAQUE_RESOURCE_ID, new byte[0]));
+        initializer.setInstancesForObject(TestLwM2mId.TEST_OBJECT, new LwM2mTestObject());
         List<LwM2mObjectEnabler> objects = initializer.createAll();
 
         // Build Client
diff --git a/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/util/QueueModeIntegrationTestHelper.java b/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/util/QueueModeIntegrationTestHelper.java
index 6005f988..9f4665bf 100644
--- a/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/util/QueueModeIntegrationTestHelper.java
+++ b/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/util/QueueModeIntegrationTestHelper.java
@@ -32,6 +32,7 @@ import org.eclipse.leshan.client.resource.ObjectsInitializer;
 import org.eclipse.leshan.core.LwM2mId;
 import org.eclipse.leshan.core.model.StaticModel;
 import org.eclipse.leshan.core.response.LwM2mResponse;
+import org.eclipse.leshan.core.util.TestLwM2mId;
 import org.eclipse.leshan.integration.tests.PresenceCounter;
 import org.eclipse.leshan.server.californium.LeshanServerBuilder;
 import org.eclipse.leshan.server.queue.StaticClientAwakeTimeProvider;
@@ -69,7 +70,7 @@ public class QueueModeIntegrationTestHelper extends IntegrationTestHelper {
         initializer.setInstancesForObject(LwM2mId.SERVER, new Server(12345, LIFETIME));
         initializer.setInstancesForObject(LwM2mId.DEVICE, new TestDevice("Eclipse Leshan", MODEL_NUMBER, "12345"));
         initializer.setClassForObject(LwM2mId.ACCESS_CONTROL, DummyInstanceEnabler.class);
-        initializer.setDummyInstancesForObject(2000);
+        initializer.setDummyInstancesForObject(TestLwM2mId.TEST_OBJECT);
         List<LwM2mObjectEnabler> objects = initializer.createAll();
 
         // Build Client
diff --git a/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/util/TestObjectLoader.java b/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/util/TestObjectLoader.java
index 59e27f19..76ce716b 100644
--- a/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/util/TestObjectLoader.java
+++ b/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/util/TestObjectLoader.java
@@ -20,7 +20,6 @@ import java.util.List;
 
 import org.eclipse.leshan.core.LwM2mId;
 import org.eclipse.leshan.core.model.LwM2mModelRepository;
-import org.eclipse.leshan.core.model.ObjectLoader;
 import org.eclipse.leshan.core.model.ObjectModel;
 
 public class TestObjectLoader {
@@ -30,7 +29,8 @@ public class TestObjectLoader {
      */
     public static List<ObjectModel> loadDefaultObject() {
         // load default object from the spec (with fixed version)
-        LwM2mModelRepository repository = new LwM2mModelRepository(ObjectLoader.loadAllDefault());
+        LwM2mModelRepository repository = new LwM2mModelRepository(
+                org.eclipse.leshan.core.util.TestObjectLoader.loadAllDefault());
         List<ObjectModel> objectModels = new ArrayList<ObjectModel>();
         objectModels.add(repository.getObjectModel(LwM2mId.SECURITY, "1.1"));
         objectModels.add(repository.getObjectModel(LwM2mId.SERVER, "1.1"));
@@ -42,6 +42,9 @@ public class TestObjectLoader {
         objectModels.add(repository.getObjectModel(LwM2mId.CONNECTIVITY_STATISTICS, "1.0"));
         objectModels.add(repository.getObjectModel(LwM2mId.OSCORE, "1.0"));
 
+        // Test object 3442
+        objectModels.add(repository.getObjectModel(3442, "1.0"));
+
         return objectModels;
     }
 }
diff --git a/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/write/WriteCompositeTest.java b/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/write/WriteCompositeTest.java
index 53f15745..8510007b 100644
--- a/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/write/WriteCompositeTest.java
+++ b/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/write/WriteCompositeTest.java
@@ -42,6 +42,7 @@ import org.eclipse.leshan.core.response.LwM2mResponse;
 import org.eclipse.leshan.core.response.ObserveResponse;
 import org.eclipse.leshan.core.response.ReadResponse;
 import org.eclipse.leshan.core.response.WriteCompositeResponse;
+import org.eclipse.leshan.core.util.TestLwM2mId;
 import org.eclipse.leshan.integration.tests.observe.TestObservationListener;
 import org.eclipse.leshan.integration.tests.util.IntegrationTestHelper;
 import org.junit.After;
@@ -58,8 +59,8 @@ public class WriteCompositeTest {
     @Parameters(name = "{0}")
     public static Collection<?> contentFormats() {
         return Arrays.asList(new Object[][] { //
-                                { ContentFormat.SENML_JSON }, //
-                                { ContentFormat.SENML_CBOR } });
+                { ContentFormat.SENML_JSON }, //
+                { ContentFormat.SENML_CBOR } });
     }
 
     private ContentFormat contentFormat;
@@ -111,8 +112,8 @@ public class WriteCompositeTest {
     public void can_write_resource_and_instance() throws InterruptedException {
         // create value
         LwM2mSingleResource utcOffset = LwM2mSingleResource.newStringResource(14, "+02");
-        LwM2mPath resourceInstancePath = new LwM2mPath(IntegrationTestHelper.TEST_OBJECT_ID, 0,
-                IntegrationTestHelper.STRING_RESOURCE_INSTANCE_ID, 100);
+        LwM2mPath resourceInstancePath = new LwM2mPath(TestLwM2mId.TEST_OBJECT, 0, TestLwM2mId.MULTIPLE_STRING_VALUE,
+                0);
         LwM2mResourceInstance testStringResourceInstance = LwM2mResourceInstance
                 .newStringInstance(resourceInstancePath.getResourceInstanceId(), "test_string_instance");
 
@@ -141,8 +142,8 @@ public class WriteCompositeTest {
     @Test
     public void can_add_resource_instances() throws InterruptedException {
         // Prepare node
-        LwM2mPath resourceInstancePath = new LwM2mPath(IntegrationTestHelper.TEST_OBJECT_ID, 0,
-                IntegrationTestHelper.STRING_RESOURCE_INSTANCE_ID, 100);
+        LwM2mPath resourceInstancePath = new LwM2mPath(TestLwM2mId.TEST_OBJECT, 0, TestLwM2mId.MULTIPLE_STRING_VALUE,
+                1);
         LwM2mResourceInstance testStringResourceInstance = LwM2mResourceInstance
                 .newStringInstance(resourceInstancePath.getResourceInstanceId(), "test_string_instance");
         Map<LwM2mPath, LwM2mNode> nodes = new HashMap<>();
@@ -161,7 +162,7 @@ public class WriteCompositeTest {
         ReadResponse readResponse = helper.server.send(helper.getCurrentRegistration(),
                 new ReadRequest(contentFormat, resourceInstancePath.toResourcePath(), null));
         LwM2mMultipleResource multiResource = (LwM2mMultipleResource) readResponse.getContent();
-        assertEquals(3, multiResource.getInstances().size());
+        assertEquals(2, multiResource.getInstances().size());
         assertEquals(testStringResourceInstance,
                 multiResource.getInstance(resourceInstancePath.getResourceInstanceId()));
     }
diff --git a/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/write/WriteMultiValueTest.java b/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/write/WriteMultiValueTest.java
index 73a76401..8bfe8126 100644
--- a/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/write/WriteMultiValueTest.java
+++ b/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/write/WriteMultiValueTest.java
@@ -41,6 +41,7 @@ import org.eclipse.leshan.core.request.WriteRequest;
 import org.eclipse.leshan.core.request.WriteRequest.Mode;
 import org.eclipse.leshan.core.response.ReadResponse;
 import org.eclipse.leshan.core.response.WriteResponse;
+import org.eclipse.leshan.core.util.TestLwM2mId;
 import org.eclipse.leshan.integration.tests.util.IntegrationTestHelper;
 import org.junit.After;
 import org.junit.Before;
@@ -56,12 +57,12 @@ public class WriteMultiValueTest {
     @Parameters(name = "{0}")
     public static Collection<?> contentFormats() {
         return Arrays.asList(new Object[][] { //
-                                { ContentFormat.TLV }, //
-                                { ContentFormat.fromCode(ContentFormat.OLD_TLV_CODE) }, //
-                                { ContentFormat.JSON }, //
-                                { ContentFormat.fromCode(ContentFormat.OLD_JSON_CODE) }, //
-                                { ContentFormat.SENML_JSON }, //
-                                { ContentFormat.SENML_CBOR } });
+                { ContentFormat.TLV }, //
+                { ContentFormat.fromCode(ContentFormat.OLD_TLV_CODE) }, //
+                { ContentFormat.JSON }, //
+                { ContentFormat.fromCode(ContentFormat.OLD_JSON_CODE) }, //
+                { ContentFormat.SENML_JSON }, //
+                { ContentFormat.SENML_CBOR } });
     }
 
     private ContentFormat contentFormat;
@@ -188,9 +189,8 @@ public class WriteMultiValueTest {
         neighbourCellReport.put(2, new ObjectLink(10244, 3));
 
         // Write objlnk resource in TLV format
-        WriteResponse response = helper.server.send(helper.getCurrentRegistration(),
-                new WriteRequest(contentFormat, IntegrationTestHelper.TEST_OBJECT_ID, 0,
-                        IntegrationTestHelper.OBJLNK_MULTI_INSTANCE_RESOURCE_ID, neighbourCellReport, Type.OBJLNK));
+        WriteResponse response = helper.server.send(helper.getCurrentRegistration(), new WriteRequest(contentFormat,
+                TestLwM2mId.TEST_OBJECT, 0, TestLwM2mId.MULTIPLE_OBJLINK_VALUE, neighbourCellReport, Type.OBJLNK));
 
         // Verify Write result
         assertEquals(ResponseCode.CHANGED, response.getCode());
@@ -198,8 +198,8 @@ public class WriteMultiValueTest {
         assertThat(response.getCoapResponse(), is(instanceOf(Response.class)));
 
         // Reading back the written OBJLNK value
-        ReadResponse readResponse = helper.server.send(helper.getCurrentRegistration(), new ReadRequest(
-                IntegrationTestHelper.TEST_OBJECT_ID, 0, IntegrationTestHelper.OBJLNK_MULTI_INSTANCE_RESOURCE_ID));
+        ReadResponse readResponse = helper.server.send(helper.getCurrentRegistration(),
+                new ReadRequest(TestLwM2mId.TEST_OBJECT, 0, TestLwM2mId.MULTIPLE_OBJLINK_VALUE));
         LwM2mMultipleResource resource = (LwM2mMultipleResource) readResponse.getContent();
 
         // verify read value
@@ -221,9 +221,8 @@ public class WriteMultiValueTest {
         values.put(10, "value10");
         values.put(20, "value20");
 
-        WriteResponse response = helper.server.send(helper.getCurrentRegistration(),
-                new WriteRequest(Mode.REPLACE, contentFormat, IntegrationTestHelper.TEST_OBJECT_ID, 0,
-                        IntegrationTestHelper.STRING_RESOURCE_INSTANCE_ID, values, Type.STRING));
+        WriteResponse response = helper.server.send(helper.getCurrentRegistration(), new WriteRequest(Mode.REPLACE,
+                contentFormat, TestLwM2mId.TEST_OBJECT, 0, TestLwM2mId.MULTIPLE_STRING_VALUE, values, Type.STRING));
 
         // Verify Write result
         assertEquals(ResponseCode.CHANGED, response.getCode());
@@ -231,8 +230,8 @@ public class WriteMultiValueTest {
         assertThat(response.getCoapResponse(), is(instanceOf(Response.class)));
 
         // read multi instance
-        ReadResponse readResponse = helper.server.send(helper.getCurrentRegistration(), new ReadRequest(contentFormat,
-                IntegrationTestHelper.TEST_OBJECT_ID, 0, IntegrationTestHelper.STRING_RESOURCE_INSTANCE_ID));
+        ReadResponse readResponse = helper.server.send(helper.getCurrentRegistration(),
+                new ReadRequest(contentFormat, TestLwM2mId.TEST_OBJECT, 0, TestLwM2mId.MULTIPLE_STRING_VALUE));
 
         // verify result
         assertEquals(CONTENT, readResponse.getCode());
@@ -248,9 +247,8 @@ public class WriteMultiValueTest {
         Map<Integer, String> newValues = new HashMap<>();
         newValues.put(20, "value200");
         newValues.put(30, "value30");
-        response = helper.server.send(helper.getCurrentRegistration(),
-                new WriteRequest(Mode.UPDATE, contentFormat, IntegrationTestHelper.TEST_OBJECT_ID, 0,
-                        IntegrationTestHelper.STRING_RESOURCE_INSTANCE_ID, newValues, Type.STRING));
+        response = helper.server.send(helper.getCurrentRegistration(), new WriteRequest(Mode.UPDATE, contentFormat,
+                TestLwM2mId.TEST_OBJECT, 0, TestLwM2mId.MULTIPLE_STRING_VALUE, newValues, Type.STRING));
 
         // Verify Write result
         assertEquals(ResponseCode.CHANGED, response.getCode());
@@ -258,8 +256,8 @@ public class WriteMultiValueTest {
         assertThat(response.getCoapResponse(), is(instanceOf(Response.class)));
 
         // read multi instance
-        readResponse = helper.server.send(helper.getCurrentRegistration(), new ReadRequest(contentFormat,
-                IntegrationTestHelper.TEST_OBJECT_ID, 0, IntegrationTestHelper.STRING_RESOURCE_INSTANCE_ID));
+        readResponse = helper.server.send(helper.getCurrentRegistration(),
+                new ReadRequest(contentFormat, TestLwM2mId.TEST_OBJECT, 0, TestLwM2mId.MULTIPLE_STRING_VALUE));
 
         // verify result
         assertEquals(CONTENT, readResponse.getCode());
@@ -275,9 +273,8 @@ public class WriteMultiValueTest {
         // --------------------------------
         newValues = new HashMap<>();
         newValues.put(1, "value1");
-        response = helper.server.send(helper.getCurrentRegistration(),
-                new WriteRequest(Mode.REPLACE, contentFormat, IntegrationTestHelper.TEST_OBJECT_ID, 0,
-                        IntegrationTestHelper.STRING_RESOURCE_INSTANCE_ID, newValues, Type.STRING));
+        response = helper.server.send(helper.getCurrentRegistration(), new WriteRequest(Mode.REPLACE, contentFormat,
+                TestLwM2mId.TEST_OBJECT, 0, TestLwM2mId.MULTIPLE_STRING_VALUE, newValues, Type.STRING));
 
         // Verify Write result
         assertEquals(ResponseCode.CHANGED, response.getCode());
@@ -285,8 +282,8 @@ public class WriteMultiValueTest {
         assertThat(response.getCoapResponse(), is(instanceOf(Response.class)));
 
         // read multi instance
-        readResponse = helper.server.send(helper.getCurrentRegistration(), new ReadRequest(contentFormat,
-                IntegrationTestHelper.TEST_OBJECT_ID, 0, IntegrationTestHelper.STRING_RESOURCE_INSTANCE_ID));
+        readResponse = helper.server.send(helper.getCurrentRegistration(),
+                new ReadRequest(contentFormat, TestLwM2mId.TEST_OBJECT, 0, TestLwM2mId.MULTIPLE_STRING_VALUE));
 
         // verify result
         assertEquals(CONTENT, readResponse.getCode());
diff --git a/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/write/WriteOpaqueValueTest.java b/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/write/WriteOpaqueValueTest.java
index 0a797e42..368b3a23 100644
--- a/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/write/WriteOpaqueValueTest.java
+++ b/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/write/WriteOpaqueValueTest.java
@@ -15,7 +15,6 @@
  *******************************************************************************/
 package org.eclipse.leshan.integration.tests.write;
 
-import static org.eclipse.leshan.integration.tests.util.IntegrationTestHelper.*;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.core.Is.is;
 import static org.hamcrest.core.IsInstanceOf.instanceOf;
@@ -36,6 +35,7 @@ import org.eclipse.leshan.core.request.ReadRequest;
 import org.eclipse.leshan.core.request.WriteRequest;
 import org.eclipse.leshan.core.response.ReadResponse;
 import org.eclipse.leshan.core.response.WriteResponse;
+import org.eclipse.leshan.core.util.TestLwM2mId;
 import org.eclipse.leshan.integration.tests.util.IntegrationTestHelper;
 import org.junit.After;
 import org.junit.Before;
@@ -51,15 +51,15 @@ public class WriteOpaqueValueTest {
     @Parameters(name = "{0}")
     public static Collection<?> contentFormats() {
         return Arrays.asList(new Object[][] { //
-                                { ContentFormat.OPAQUE }, //
-                                { ContentFormat.TEXT }, //
-                                { ContentFormat.TLV }, //
-                                { ContentFormat.CBOR }, //
-                                { ContentFormat.fromCode(ContentFormat.OLD_TLV_CODE) }, //
-                                { ContentFormat.JSON }, //
-                                { ContentFormat.fromCode(ContentFormat.OLD_JSON_CODE) }, //
-                                { ContentFormat.SENML_JSON }, //
-                                { ContentFormat.SENML_CBOR } });
+                { ContentFormat.OPAQUE }, //
+                { ContentFormat.TEXT }, //
+                { ContentFormat.TLV }, //
+                { ContentFormat.CBOR }, //
+                { ContentFormat.fromCode(ContentFormat.OLD_TLV_CODE) }, //
+                { ContentFormat.JSON }, //
+                { ContentFormat.fromCode(ContentFormat.OLD_JSON_CODE) }, //
+                { ContentFormat.SENML_JSON }, //
+                { ContentFormat.SENML_CBOR } });
     }
 
     private ContentFormat contentFormat;
@@ -90,7 +90,7 @@ public class WriteOpaqueValueTest {
         // write resource
         byte[] expectedvalue = new byte[] { 1, 2, 3 };
         WriteResponse response = helper.server.send(helper.getCurrentRegistration(),
-                new WriteRequest(contentFormat, TEST_OBJECT_ID, 0, OPAQUE_RESOURCE_ID, expectedvalue));
+                new WriteRequest(contentFormat, TestLwM2mId.TEST_OBJECT, 0, TestLwM2mId.OPAQUE_VALUE, expectedvalue));
 
         // verify result
         assertEquals(ResponseCode.CHANGED, response.getCode());
@@ -99,7 +99,7 @@ public class WriteOpaqueValueTest {
 
         // read resource to check the value changed
         ReadResponse readResponse = helper.server.send(helper.getCurrentRegistration(),
-                new ReadRequest(contentFormat, TEST_OBJECT_ID, 0, OPAQUE_RESOURCE_ID));
+                new ReadRequest(contentFormat, TestLwM2mId.TEST_OBJECT, 0, TestLwM2mId.OPAQUE_VALUE));
         LwM2mResource resource = (LwM2mResource) readResponse.getContent();
         assertArrayEquals(expectedvalue, (byte[]) resource.getValue());
     }
@@ -114,7 +114,7 @@ public class WriteOpaqueValueTest {
         values.put(3, secondExpectedvalue);
 
         WriteResponse response = helper.server.send(helper.getCurrentRegistration(), new WriteRequest(ContentFormat.TLV,
-                TEST_OBJECT_ID, 0, OPAQUE_MULTI_INSTANCE_RESOURCE_ID, values, Type.OPAQUE));
+                TestLwM2mId.TEST_OBJECT, 0, TestLwM2mId.MULTIPLE_OPAQUE_VALUE, values, Type.OPAQUE));
 
         // verify result
         assertEquals(ResponseCode.CHANGED, response.getCode());
@@ -123,20 +123,20 @@ public class WriteOpaqueValueTest {
 
         // read first instance using parameter content format
         ReadResponse readResponse = helper.server.send(helper.getCurrentRegistration(),
-                new ReadRequest(contentFormat, TEST_OBJECT_ID, 0, OPAQUE_MULTI_INSTANCE_RESOURCE_ID, 2));
+                new ReadRequest(contentFormat, TestLwM2mId.TEST_OBJECT, 0, TestLwM2mId.MULTIPLE_OPAQUE_VALUE, 2));
         LwM2mResourceInstance resource = (LwM2mResourceInstance) readResponse.getContent();
         assertArrayEquals(firstExpectedvalue, (byte[]) resource.getValue());
 
         // read second instance using parameter content format
         readResponse = helper.server.send(helper.getCurrentRegistration(),
-                new ReadRequest(contentFormat, TEST_OBJECT_ID, 0, OPAQUE_MULTI_INSTANCE_RESOURCE_ID, 3));
+                new ReadRequest(contentFormat, TestLwM2mId.TEST_OBJECT, 0, TestLwM2mId.MULTIPLE_OPAQUE_VALUE, 3));
         resource = (LwM2mResourceInstance) readResponse.getContent();
         assertArrayEquals(secondExpectedvalue, (byte[]) resource.getValue());
 
         // write second resource instance using parameter content format
         byte[] newExpectedvalue = new byte[] { 7, 8, 9, 10 };
-        response = helper.server.send(helper.getCurrentRegistration(), new WriteRequest(contentFormat, TEST_OBJECT_ID,
-                0, OPAQUE_MULTI_INSTANCE_RESOURCE_ID, 3, newExpectedvalue, Type.OPAQUE));
+        response = helper.server.send(helper.getCurrentRegistration(), new WriteRequest(contentFormat,
+                TestLwM2mId.TEST_OBJECT, 0, TestLwM2mId.MULTIPLE_OPAQUE_VALUE, 3, newExpectedvalue, Type.OPAQUE));
 
         // verify result
         assertEquals(ResponseCode.CHANGED, response.getCode());
@@ -145,7 +145,7 @@ public class WriteOpaqueValueTest {
 
         // read second instance using parameter content format
         readResponse = helper.server.send(helper.getCurrentRegistration(),
-                new ReadRequest(contentFormat, TEST_OBJECT_ID, 0, OPAQUE_MULTI_INSTANCE_RESOURCE_ID, 3));
+                new ReadRequest(contentFormat, TestLwM2mId.TEST_OBJECT, 0, TestLwM2mId.MULTIPLE_OPAQUE_VALUE, 3));
         resource = (LwM2mResourceInstance) readResponse.getContent();
         assertArrayEquals(newExpectedvalue, (byte[]) resource.getValue());
     }
diff --git a/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/write/WriteSingleValueTest.java b/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/write/WriteSingleValueTest.java
index 1724361a..542b45de 100644
--- a/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/write/WriteSingleValueTest.java
+++ b/leshan-integration-tests/src/test/java/org/eclipse/leshan/integration/tests/write/WriteSingleValueTest.java
@@ -16,7 +16,6 @@
 package org.eclipse.leshan.integration.tests.write;
 
 import static org.eclipse.leshan.core.ResponseCode.CHANGED;
-import static org.eclipse.leshan.integration.tests.util.IntegrationTestHelper.*;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.core.Is.is;
 import static org.hamcrest.core.IsInstanceOf.instanceOf;
@@ -41,6 +40,7 @@ import org.eclipse.leshan.core.response.ErrorCallback;
 import org.eclipse.leshan.core.response.ReadResponse;
 import org.eclipse.leshan.core.response.ResponseCallback;
 import org.eclipse.leshan.core.response.WriteResponse;
+import org.eclipse.leshan.core.util.TestLwM2mId;
 import org.eclipse.leshan.core.util.datatype.ULong;
 import org.eclipse.leshan.integration.tests.util.IntegrationTestHelper;
 import org.junit.After;
@@ -57,14 +57,14 @@ public class WriteSingleValueTest {
     @Parameters(name = "{0}")
     public static Collection<?> contentFormats() {
         return Arrays.asList(new Object[][] { //
-                                { ContentFormat.TEXT }, //
-                                { ContentFormat.TLV }, //
-                                { ContentFormat.CBOR }, //
-                                { ContentFormat.fromCode(ContentFormat.OLD_TLV_CODE) }, //
-                                { ContentFormat.JSON }, //
-                                { ContentFormat.fromCode(ContentFormat.OLD_JSON_CODE) }, //
-                                { ContentFormat.SENML_JSON }, //
-                                { ContentFormat.SENML_CBOR } });
+                { ContentFormat.TEXT }, //
+                { ContentFormat.TLV }, //
+                { ContentFormat.CBOR }, //
+                { ContentFormat.fromCode(ContentFormat.OLD_TLV_CODE) }, //
+                { ContentFormat.JSON }, //
+                { ContentFormat.fromCode(ContentFormat.OLD_JSON_CODE) }, //
+                { ContentFormat.SENML_JSON }, //
+                { ContentFormat.SENML_CBOR } });
     }
 
     private ContentFormat contentFormat;
@@ -95,7 +95,7 @@ public class WriteSingleValueTest {
         // write resource
         String expectedvalue = "stringvalue";
         WriteResponse response = helper.server.send(helper.getCurrentRegistration(),
-                new WriteRequest(contentFormat, TEST_OBJECT_ID, 0, STRING_RESOURCE_ID, expectedvalue));
+                new WriteRequest(contentFormat, TestLwM2mId.TEST_OBJECT, 0, TestLwM2mId.STRING_VALUE, expectedvalue));
 
         // verify result
         assertEquals(ResponseCode.CHANGED, response.getCode());
@@ -104,7 +104,7 @@ public class WriteSingleValueTest {
 
         // read resource to check the value changed
         ReadResponse readResponse = helper.server.send(helper.getCurrentRegistration(),
-                new ReadRequest(contentFormat, TEST_OBJECT_ID, 0, STRING_RESOURCE_ID));
+                new ReadRequest(contentFormat, TestLwM2mId.TEST_OBJECT, 0, TestLwM2mId.STRING_VALUE));
         LwM2mResource resource = (LwM2mResource) readResponse.getContent();
         assertEquals(expectedvalue, resource.getValue());
     }
@@ -114,7 +114,7 @@ public class WriteSingleValueTest {
         // write resource
         boolean expectedvalue = true;
         WriteResponse response = helper.server.send(helper.getCurrentRegistration(),
-                new WriteRequest(contentFormat, TEST_OBJECT_ID, 0, BOOLEAN_RESOURCE_ID, expectedvalue));
+                new WriteRequest(contentFormat, TestLwM2mId.TEST_OBJECT, 0, TestLwM2mId.BOOLEAN_VALUE, expectedvalue));
 
         // verify result
         assertEquals(ResponseCode.CHANGED, response.getCode());
@@ -123,7 +123,7 @@ public class WriteSingleValueTest {
 
         // read resource to check the value changed
         ReadResponse readResponse = helper.server.send(helper.getCurrentRegistration(),
-                new ReadRequest(contentFormat, TEST_OBJECT_ID, 0, BOOLEAN_RESOURCE_ID));
+                new ReadRequest(contentFormat, TestLwM2mId.TEST_OBJECT, 0, TestLwM2mId.BOOLEAN_VALUE));
         LwM2mResource resource = (LwM2mResource) readResponse.getContent();
         assertEquals(expectedvalue, resource.getValue());
     }
@@ -133,7 +133,7 @@ public class WriteSingleValueTest {
         // write resource
         long expectedvalue = -999l;
         WriteResponse response = helper.server.send(helper.getCurrentRegistration(),
-                new WriteRequest(contentFormat, TEST_OBJECT_ID, 0, INTEGER_RESOURCE_ID, expectedvalue));
+                new WriteRequest(contentFormat, TestLwM2mId.TEST_OBJECT, 0, TestLwM2mId.INTEGER_VALUE, expectedvalue));
 
         // verify result
         assertEquals(ResponseCode.CHANGED, response.getCode());
@@ -142,7 +142,7 @@ public class WriteSingleValueTest {
 
         // read resource to check the value changed
         ReadResponse readResponse = helper.server.send(helper.getCurrentRegistration(),
-                new ReadRequest(contentFormat, TEST_OBJECT_ID, 0, INTEGER_RESOURCE_ID));
+                new ReadRequest(contentFormat, TestLwM2mId.TEST_OBJECT, 0, TestLwM2mId.INTEGER_VALUE));
         LwM2mResource resource = (LwM2mResource) readResponse.getContent();
         assertEquals(expectedvalue, resource.getValue());
     }
@@ -150,15 +150,15 @@ public class WriteSingleValueTest {
     @Test
     public void can_write_string_resource_instance() throws InterruptedException {
         write_string_resource_instance(contentFormat, 0);
-        write_string_resource_instance(contentFormat, 1);
     }
 
     private void write_string_resource_instance(ContentFormat format, int resourceInstance)
             throws InterruptedException {
         // read device model number
         String valueToWrite = "newValue";
-        WriteResponse response = helper.server.send(helper.getCurrentRegistration(), new WriteRequest(format,
-                TEST_OBJECT_ID, 0, STRING_RESOURCE_INSTANCE_ID, resourceInstance, valueToWrite, Type.STRING));
+        WriteResponse response = helper.server.send(helper.getCurrentRegistration(),
+                new WriteRequest(format, TestLwM2mId.TEST_OBJECT, 0, TestLwM2mId.MULTIPLE_STRING_VALUE,
+                        resourceInstance, valueToWrite, Type.STRING));
 
         // verify result
         assertEquals(CHANGED, response.getCode());
@@ -166,8 +166,8 @@ public class WriteSingleValueTest {
         assertThat(response.getCoapResponse(), is(instanceOf(Response.class)));
 
         // read resource to check the value changed
-        ReadResponse readResponse = helper.server.send(helper.getCurrentRegistration(),
-                new ReadRequest(format, TEST_OBJECT_ID, 0, STRING_RESOURCE_INSTANCE_ID, resourceInstance));
+        ReadResponse readResponse = helper.server.send(helper.getCurrentRegistration(), new ReadRequest(format,
+                TestLwM2mId.TEST_OBJECT, 0, TestLwM2mId.MULTIPLE_STRING_VALUE, resourceInstance));
 
         // verify result
         LwM2mResourceInstance resource = (LwM2mResourceInstance) readResponse.getContent();
@@ -179,7 +179,7 @@ public class WriteSingleValueTest {
         // write resource
         double expectedvalue = 999.99;
         WriteResponse response = helper.server.send(helper.getCurrentRegistration(),
-                new WriteRequest(contentFormat, TEST_OBJECT_ID, 0, FLOAT_RESOURCE_ID, expectedvalue));
+                new WriteRequest(contentFormat, TestLwM2mId.TEST_OBJECT, 0, TestLwM2mId.FLOAT_VALUE, expectedvalue));
 
         // verify result
         assertEquals(ResponseCode.CHANGED, response.getCode());
@@ -188,7 +188,7 @@ public class WriteSingleValueTest {
 
         // read resource to check the value changed
         ReadResponse readResponse = helper.server.send(helper.getCurrentRegistration(),
-                new ReadRequest(contentFormat, TEST_OBJECT_ID, 0, FLOAT_RESOURCE_ID));
+                new ReadRequest(contentFormat, TestLwM2mId.TEST_OBJECT, 0, TestLwM2mId.FLOAT_VALUE));
         LwM2mResource resource = (LwM2mResource) readResponse.getContent();
         assertEquals(expectedvalue, resource.getValue());
     }
@@ -198,7 +198,8 @@ public class WriteSingleValueTest {
         // write resource
         Date expectedvalue = new Date(946681000l); // second accuracy
         WriteResponse response = helper.server.send(helper.getCurrentRegistration(),
-                new WriteRequest(contentFormat, TEST_OBJECT_ID, 0, TIME_RESOURCE_ID, expectedvalue), 100000000);
+                new WriteRequest(contentFormat, TestLwM2mId.TEST_OBJECT, 0, TestLwM2mId.TIME_VALUE, expectedvalue),
+                100000000);
 
         // verify result
         assertEquals(ResponseCode.CHANGED, response.getCode());
@@ -207,7 +208,7 @@ public class WriteSingleValueTest {
 
         // read resource to check the value changed
         ReadResponse readResponse = helper.server.send(helper.getCurrentRegistration(),
-                new ReadRequest(contentFormat, TEST_OBJECT_ID, 0, TIME_RESOURCE_ID), 100000000);
+                new ReadRequest(contentFormat, TestLwM2mId.TEST_OBJECT, 0, TestLwM2mId.TIME_VALUE), 100000000);
         LwM2mResource resource = (LwM2mResource) readResponse.getContent();
         assertEquals(expectedvalue, resource.getValue());
     }
@@ -217,8 +218,8 @@ public class WriteSingleValueTest {
         // write resource
         ULong expectedvalue = ULong.valueOf("18446744073709551615"); // this unsigned integer can not be stored in a
 
-        WriteResponse response = helper.server.send(helper.getCurrentRegistration(),
-                new WriteRequest(contentFormat, TEST_OBJECT_ID, 0, UNSIGNED_INTEGER_RESOURCE_ID, expectedvalue));
+        WriteResponse response = helper.server.send(helper.getCurrentRegistration(), new WriteRequest(contentFormat,
+                TestLwM2mId.TEST_OBJECT, 0, TestLwM2mId.UNSIGNED_INTEGER_VALUE, expectedvalue));
 
         // verify result
         assertEquals(ResponseCode.CHANGED, response.getCode());
@@ -227,7 +228,7 @@ public class WriteSingleValueTest {
 
         // read resource to check the value changed
         ReadResponse readResponse = helper.server.send(helper.getCurrentRegistration(),
-                new ReadRequest(TEST_OBJECT_ID, 0, UNSIGNED_INTEGER_RESOURCE_ID));
+                new ReadRequest(TestLwM2mId.TEST_OBJECT, 0, TestLwM2mId.UNSIGNED_INTEGER_VALUE));
         LwM2mResource resource = (LwM2mResource) readResponse.getContent();
         assertEquals(expectedvalue, resource.getValue());
     }
@@ -239,8 +240,7 @@ public class WriteSingleValueTest {
 
         // Write objlnk resource
         WriteResponse response = helper.server.send(helper.getCurrentRegistration(),
-                new WriteRequest(contentFormat, IntegrationTestHelper.TEST_OBJECT_ID, 0,
-                        IntegrationTestHelper.OBJLNK_SINGLE_INSTANCE_RESOURCE_ID, data));
+                new WriteRequest(contentFormat, TestLwM2mId.TEST_OBJECT, 0, TestLwM2mId.MULTIPLE_OBJLINK_VALUE, data));
 
         // Verify Write result
         assertEquals(ResponseCode.CHANGED, response.getCode());
@@ -248,8 +248,8 @@ public class WriteSingleValueTest {
         assertThat(response.getCoapResponse(), is(instanceOf(Response.class)));
 
         // Reading back the written OBJLNK value
-        ReadResponse readResponse = helper.server.send(helper.getCurrentRegistration(), new ReadRequest(
-                IntegrationTestHelper.TEST_OBJECT_ID, 0, IntegrationTestHelper.OBJLNK_SINGLE_INSTANCE_RESOURCE_ID));
+        ReadResponse readResponse = helper.server.send(helper.getCurrentRegistration(),
+                new ReadRequest(TestLwM2mId.TEST_OBJECT, 0, TestLwM2mId.MULTIPLE_OBJLINK_VALUE));
         LwM2mSingleResource resource = (LwM2mSingleResource) readResponse.getContent();
 
         // verify read value