diff --git a/common/source/java/ch/systemsx/cisd/common/test/EqualsHashCodeTestCase.java b/common/source/java/ch/systemsx/cisd/common/test/EqualsHashCodeTestCase.java
new file mode 100644
index 0000000000000000000000000000000000000000..ed7cd5f123f9d76ca32c22e03647371c5bcf794b
--- /dev/null
+++ b/common/source/java/ch/systemsx/cisd/common/test/EqualsHashCodeTestCase.java
@@ -0,0 +1,254 @@
+/*
+ * The JUnit-addons Software License, Version 1.0
+ *     (based on the Apache Software License, Version 1.1)
+ *
+ * Copyright (c) 2002-2003 Vladimir R. Bossicard.  All rights reserved.
+ *
+ * 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. The end-user documentation included with the redistribution, if
+ *    any, must include the following acknowlegement:
+ *       "This product includes software developed by Vladimir R.
+ *        Bossicard as well as other contributors
+ *        (http://junit-addons.sourceforge.net/)."
+ *    Alternately, this acknowlegement may appear in the software itself,
+ *    if and wherever such third-party acknowlegements normally appear.
+ *
+ * 4. The name "JUnit-addons" must not be used to endorse or promote
+ *    products derived from this software without prior written
+ *    permission. For written permission, please contact
+ *    vbossica@users.sourceforge.net.
+ *
+ * 5. Products derived from this software may not be called "JUnit-addons"
+ *    nor may "JUnit-addons" appear in their names without prior written
+ *    permission of the project managers.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED 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 APACHE SOFTWARE FOUNDATION OR
+ * ITS 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.
+ * ======================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals.  For more information on the JUnit-addons Project, please
+ * see <http://junit-addons.sourceforge.net/>.
+ */
+
+package ch.systemsx.cisd.common.test;
+
+import static org.testng.Assert.*;
+
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+/**
+ * Extend me in order to test a class's functional compliance with the <code>equals</code> and <code>hashCode</code>
+ * contract.
+ * <p>
+ * Override my {@link #createInstance() createInstance} and {@link #createNotEqualInstance() createNotEqualInstance}
+ * methods to provide me with objects to test against. Both methods should return objects that are of the same class.
+ * <p>
+ * <b>WARNING</b>: Extend me only if your class overrides <code>equals</code> to test for equivalence. If your
+ * class's <code>equals</code> tests for identity or preserves the behavior from <code>Object</code>, I'm not
+ * interested, because I expect <code>createInstance</code> to return equivalent but distinct objects.
+ * 
+ * @see java.lang.Object#equals(Object)
+ * @see java.lang.Object#hashCode()
+ * @author Christian Ribeaud
+ */
+public abstract class EqualsHashCodeTestCase
+{
+
+    private Object eq1;
+
+    private Object eq2;
+
+    private Object eq3;
+
+    private Object neq;
+
+    private static final int NUM_ITERATIONS = 20;
+
+    /**
+     * Creates and returns an instance of the class under test.
+     * 
+     * @return a new instance of the class under test; each object returned from this method should compare equal to
+     *         each other.
+     * @throws Exception
+     */
+    protected abstract Object createInstance() throws Exception;
+
+    /**
+     * Creates and returns an instance of the class under test.
+     * 
+     * @return a new instance of the class under test; each object returned from this method should compare equal to
+     *         each other, but not to the objects returned from {@link #createInstance() createInstance}.
+     * @throws Exception
+     */
+    protected abstract Object createNotEqualInstance() throws Exception;
+
+    /**
+     * Sets up the test fixture.
+     * 
+     * @throws Exception
+     */
+    @BeforeClass
+    public final void setUp() throws Exception
+    {
+        eq1 = createInstance();
+        eq2 = createInstance();
+        eq3 = createInstance();
+        neq = createNotEqualInstance();
+
+        assert eq1 != null : "createInstance() returned null";
+        assert eq2 != null : "2nd createInstance() returned null";
+        assert eq3 != null : "3rd createInstance() returned null";
+        assert neq != null : "createNotEqualInstance() returned null";
+
+        assertNotSame(eq1, eq2);
+        assertNotSame(eq1, eq3);
+        assertNotSame(eq1, neq);
+        assertNotSame(eq2, eq3);
+        assertNotSame(eq2, neq);
+        assertNotSame(eq3, neq);
+
+        assert eq1.getClass().equals(eq2.getClass()) : "1st and 2nd equal instances of different classes";
+        assert eq1.getClass().equals(eq3.getClass()) : "1st and 3nd equal instances of different classes";
+        assert eq1.getClass().equals(neq.getClass()) : "1st equal instance and not-equal instance of different classes";
+    }
+
+    /**
+     * Tests whether <code>equals</code> holds up against a new <code>Object</code> (should always be
+     * <code>false</code>).
+     */
+    @Test
+    public final void testEqualsAgainstNewObject()
+    {
+        Object o = new Object();
+        assertNotSame(eq1, o);
+        assertNotSame(eq2, o);
+        assertNotSame(eq3, o);
+        assertNotSame(neq, o);
+    }
+
+    /**
+     * Tests whether <code>equals</code> holds up against <code>null</code>.
+     */
+    @Test
+    public final void testEqualsAgainstNull()
+    {
+        assertNotSame("1st vs. null", eq1, null);
+        assertNotSame("2nd vs. null", eq2, null);
+        assertNotSame("3rd vs. null", eq3, null);
+        assertNotSame("not-equal vs. null", neq, null);
+    }
+
+    /**
+     * Tests whether <code>equals</code> holds up against objects that should not compare equal.
+     */
+    @Test
+    public final void testEqualsAgainstUnequalObjects()
+    {
+        assert eq1.equals(neq) == false : "1st vs. not-equal";
+        assert eq2.equals(neq) == false : "2nd vs. not-equal";
+        assert eq3.equals(neq) == false : "3rd vs. not-equal";
+
+        assert neq.equals(eq1) == false : "not-equal vs. 1st";
+        assert neq.equals(eq2) == false : "not-equal vs. 2nd";
+        assert neq.equals(eq3) == false : "not-equal vs. 3rd";
+    }
+
+    /**
+     * Tests whether <code>equals</code> is <em>consistent</em>.
+     */
+    @Test
+    public final void testEqualsIsConsistentAcrossInvocations()
+    {
+        for (int i = 0; i < NUM_ITERATIONS; ++i)
+        {
+            testEqualsAgainstNewObject();
+            testEqualsAgainstNull();
+            testEqualsAgainstUnequalObjects();
+            testEqualsIsReflexive();
+            testEqualsIsSymmetricAndTransitive();
+        }
+    }
+
+    /**
+     * Tests whether <code>equals</code> is <em>reflexive</em>.
+     */
+    @Test
+    public final void testEqualsIsReflexive()
+    {
+        assert eq1.equals(eq1) : "1st equal instance";
+        assert eq2.equals(eq2) : "2nd equal instance";
+        assert eq3.equals(eq3) : "3rd equal instance";
+        assert neq.equals(neq) : "not-equal equal instance";
+    }
+
+    /**
+     * Tests whether <code>equals</code> is <em>symmetric</em> and <em>transitive</em>.
+     */
+    @Test
+    public final void testEqualsIsSymmetricAndTransitive()
+    {
+        assert eq1.equals(eq2) : "1st vs. 2nd";
+        assert eq2.equals(eq1) : "2nd vs. 1st";
+
+        assert eq1.equals(eq3) : "1st vs. 3rd";
+        assert eq3.equals(eq1) : "3rd vs. 1st";
+
+        assert eq2.equals(eq3) : "2nd vs. 3rd";
+        assert eq3.equals(eq2) : "3rd vs. 2nd";
+    }
+
+    /**
+     * Tests the <code>hashCode</code> contract.
+     */
+    @Test
+    public final void testHashCodeContract()
+    {
+        assert eq1.hashCode() == eq2.hashCode() : "1st vs. 2nd";
+        assert eq1.hashCode() == eq3.hashCode() : "1st vs. 3rd";
+        assert eq2.hashCode() == eq3.hashCode() : "2nd vs. 3rd";
+    }
+
+    /**
+     * Tests the consistency of <code>hashCode</code>.
+     */
+    @Test
+    public final void testHashCodeIsConsistentAcrossInvocations()
+    {
+        int eq1Hash = eq1.hashCode();
+        int eq2Hash = eq2.hashCode();
+        int eq3Hash = eq3.hashCode();
+        int neqHash = neq.hashCode();
+
+        for (int i = 0; i < NUM_ITERATIONS; ++i)
+        {
+            assert eq1Hash == eq1.hashCode() : "1st equal instance";
+            assert eq2Hash == eq2.hashCode() : "2nd equal instance";
+            assert eq3Hash == eq3.hashCode() : "3rd equal instance";
+            assert neqHash == neq.hashCode() : "not-equal instance";
+        }
+    }
+
+}
diff --git a/common/source/java/ch/systemsx/cisd/common/test/SerializabilityTest.java b/common/source/java/ch/systemsx/cisd/common/test/SerializabilityTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..5d18dbcf9e8661f787cd3796a27069e3c30c66fa
--- /dev/null
+++ b/common/source/java/ch/systemsx/cisd/common/test/SerializabilityTest.java
@@ -0,0 +1,66 @@
+package ch.systemsx.cisd.common.test;
+
+import java.io.Serializable;
+
+import org.apache.commons.lang.SerializationUtils;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+/**
+ * Extend me in order to test the serializability of a class. Override my {@link #createInstance() createInstance}
+ * methods to provide me with an object to test against. The object's class must implement
+ * {@link java.io.Serializable Serializable}.
+ * 
+ * @see java.io.Serializable
+ * @author Christian Ribeaud
+ */
+public abstract class SerializabilityTest
+{
+
+    private Serializable obj;
+
+    /**
+     * Creates and returns an instance of the class under test.
+     * 
+     * @return a new instance of the class under test
+     * @throws Exception
+     */
+    protected abstract Serializable createInstance() throws Exception;
+
+    /**
+     * Sets up the test fixture.
+     * 
+     * @throws Exception
+     */
+    @BeforeClass
+    public final void setUp() throws Exception
+    {
+        obj = createInstance();
+        assert obj != null : "createInstance() returned null";
+    }
+
+    /**
+     * Verifies that an instance of the class under test can be serialized and deserialized without error.
+     */
+    @Test
+    public final void testSerializability() throws Exception
+    {
+        byte[] serial = SerializationUtils.serialize(obj);
+        Serializable deserial = (Serializable) SerializationUtils.deserialize(serial);
+        checkThawedObject(obj, deserial);
+    }
+
+    /**
+     * Template method--override this to perform checks on the deserialized form of the object serialized in
+     * {@link #testSerializability}. If not overridden, this asserts that the pre-serialization and deserialized forms
+     * of the object compare equal via {@link java.lang.Object#equals(Object) equals}.
+     * 
+     * @param expected the pre-serialization form of the object
+     * @param actual the deserialized form of the object
+     */
+    public void checkThawedObject(Serializable expected, Serializable actual) throws Exception
+    {
+        assert expected.equals(actual) : "thawed object comparison";
+    }
+
+}