Index: trunk/test/unit/org/openstreetmap/josm/gui/layer/LayerManagerTest.java
===================================================================
--- trunk/test/unit/org/openstreetmap/josm/gui/layer/LayerManagerTest.java	(revision 10396)
+++ trunk/test/unit/org/openstreetmap/josm/gui/layer/LayerManagerTest.java	(revision 10397)
@@ -2,4 +2,5 @@
 package org.openstreetmap.josm.gui.layer;
 
+import static org.hamcrest.CoreMatchers.any;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -11,4 +12,5 @@
 
 import java.awt.Graphics2D;
+import java.lang.reflect.InvocationTargetException;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -18,4 +20,5 @@
 
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.openstreetmap.josm.data.Bounds;
@@ -27,5 +30,7 @@
 import org.openstreetmap.josm.gui.layer.LayerManager.LayerRemoveEvent;
 import org.openstreetmap.josm.gui.util.GuiHelper;
+import org.openstreetmap.josm.testutils.ExpectedRootException;
 import org.openstreetmap.josm.tools.Predicates;
+import org.openstreetmap.josm.tools.bugreport.ReportedException;
 
 /**
@@ -119,8 +124,13 @@
             layerOrderChanged = e;
         }
-
     }
 
     protected LayerManager layerManager;
+
+    /**
+     * Rule used to expect exceptions.
+     */
+    @Rule
+    public ExpectedRootException thrown = ExpectedRootException.none();
 
     /**
@@ -182,6 +192,10 @@
      * {@link LayerManager#addLayer(Layer)}: duplicate layers
      */
-    @Test(expected = IllegalArgumentException.class)
+    @Test
     public void testAddLayerFails() {
+        thrown.expect(ReportedException.class);
+        thrown.expectCause(any(InvocationTargetException.class));
+        thrown.expectRootCause(any(IllegalArgumentException.class));
+
         AbstractTestLayer layer1 = new AbstractTestLayer();
         layerManager.addLayer(layer1);
@@ -192,6 +206,10 @@
      * {@link LayerManager#addLayer(Layer)}: illegal default layer position
      */
-    @Test(expected = IndexOutOfBoundsException.class)
+    @Test
     public void testAddLayerIllegalPosition() {
+        thrown.expect(ReportedException.class);
+        thrown.expectCause(any(InvocationTargetException.class));
+        thrown.expectRootCause(any(IndexOutOfBoundsException.class));
+
         AbstractTestLayer layer1 = new AbstractTestLayer() {
             @Override
@@ -260,6 +278,10 @@
      * {@link LayerManager#moveLayer(Layer, int)} fails for wrong index
      */
-    @Test(expected = IndexOutOfBoundsException.class)
+    @Test
     public void testMoveLayerFailsRange() {
+        thrown.expect(ReportedException.class);
+        thrown.expectCause(any(InvocationTargetException.class));
+        thrown.expectRootCause(any(IndexOutOfBoundsException.class));
+
         AbstractTestLayer layer1 = new AbstractTestLayer();
         AbstractTestLayer layer2 = new AbstractTestLayer();
@@ -272,6 +294,10 @@
      * {@link LayerManager#moveLayer(Layer, int)} fails for wrong layer
      */
-    @Test(expected = IllegalArgumentException.class)
+    @Test
     public void testMoveLayerFailsNotInList() {
+        thrown.expect(ReportedException.class);
+        thrown.expectCause(any(InvocationTargetException.class));
+        thrown.expectRootCause(any(IllegalArgumentException.class));
+
         AbstractTestLayer layer1 = new AbstractTestLayer();
         AbstractTestLayer layer2 = new AbstractTestLayer();
Index: trunk/test/unit/org/openstreetmap/josm/testutils/ExpectedRootException.java
===================================================================
--- trunk/test/unit/org/openstreetmap/josm/testutils/ExpectedRootException.java	(revision 10397)
+++ trunk/test/unit/org/openstreetmap/josm/testutils/ExpectedRootException.java	(revision 10397)
@@ -0,0 +1,117 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.testutils;
+
+import static org.openstreetmap.josm.testutils.ThrowableRootCauseMatcher.hasRootCause;
+
+import org.hamcrest.Matcher;
+import org.junit.rules.ExpectedException;
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+
+/**
+ * The {@code ExpectedRootException} behaves exactly as JUnit's {@link ExpectedException} rule.
+ * This class is needed to add {@link #expectRootCause} method, which has been rejected by JUnit developers,
+ * and {@code ExpectedException} cannot be extended because it has a private constructor.
+ * @see <a href="https://github.com/junit-team/junit4/pull/778">Github pull request</a>
+ */
+public final class ExpectedRootException implements TestRule {
+
+    private final ExpectedException rule = ExpectedException.none();
+
+    /**
+     * Returns a {@linkplain TestRule rule} that expects no exception to be thrown (identical to behavior without this rule).
+     * @return {@code ExpectedRootException} instance
+     */
+    @SuppressFBWarnings("NM_CLASS_NOT_EXCEPTION")
+    public static ExpectedRootException none() {
+        return new ExpectedRootException();
+    }
+
+    private ExpectedRootException() {
+    }
+
+    /**
+     * Specifies the failure message for tests that are expected to throw an exception but do not throw any.
+     * You can use a {@code %s} placeholder for the description of the expected exception.
+     * E.g. "Test doesn't throw %s." will fail with the error message "Test doesn't throw an instance of foo.".
+     *
+     * @param message exception detail message
+     * @return the rule itself
+     */
+    public ExpectedRootException reportMissingExceptionWithMessage(String message) {
+        rule.reportMissingExceptionWithMessage(message);
+        return this;
+    }
+
+    @Override
+    public Statement apply(Statement base, Description description) {
+        return rule.apply(base, description);
+    }
+
+    /**
+     * Verify that your code throws an exception that is an instance of specific {@code type}.
+     * <pre> &#064;Test
+     * public void throwsExceptionWithSpecificType() {
+     *     thrown.expect(NullPointerException.class);
+     *     throw new NullPointerException();
+     * }</pre>
+     * @param type Throwable type
+     * @return {@code this}
+     */
+    public ExpectedRootException expect(Class<? extends Throwable> type) {
+        rule.expect(type);
+        return this;
+    }
+
+    /**
+     * Verify that your code throws an exception whose message contains a specific text.
+     * <pre> &#064;Test
+     * public void throwsExceptionWhoseMessageContainsSpecificText() {
+     *     thrown.expectMessage(&quot;happened&quot;);
+     *     throw new NullPointerException(&quot;What happened?&quot;);
+     * }</pre>
+     * @param substring substring to expect in error message
+     * @return {@code this}
+     */
+    public ExpectedRootException expectMessage(String substring) {
+        rule.expectMessage(substring);
+        return this;
+    }
+
+    /**
+     * Verify that your code throws an exception whose immediate cause is matched by the given Hamcrest matcher.
+     * <pre> &#064;Test
+     * public void throwsExceptionWhoseCauseCompliesWithMatcher() {
+     *     NullPointerException rootCause = new NullPointerException();
+     *     IllegalStateException immediateCause = new IllegalStateException(rootCause);
+     *     thrown.expectCause(isA(NullPointerException.class));
+     *     throw new IllegalArgumentException(immediateCause);
+     * }</pre>
+     * @param expectedCause expected cause
+     * @return {@code this}
+     */
+    public ExpectedRootException expectCause(Matcher<? extends Throwable> expectedCause) {
+        rule.expectCause(expectedCause);
+        return this;
+    }
+
+    /**
+     * Verify that you code throws an exception whose root cause is matched by the given Hamcrest matcher.
+     * <pre> &#064;Test
+     * public void throwsExceptionWhoseRootCauseCompliesWithMatcher() {
+     *     NullPointerException rootCause = new NullPointerException();
+     *     IllegalStateException immediateCause = new IllegalStateException(rootCause);
+     *     thrown.expectRootCause(isA(NullPointerException.class));
+     *     throw new IllegalArgumentException(immediateCause);
+     * }</pre>
+     * @param expectedRootCause expected root cause
+     * @return {@code this}
+     */
+    public ExpectedRootException expectRootCause(Matcher<? extends Throwable> expectedRootCause) {
+        rule.expect(hasRootCause(expectedRootCause));
+        return this;
+    }
+}
Index: trunk/test/unit/org/openstreetmap/josm/testutils/ThrowableRootCauseMatcher.java
===================================================================
--- trunk/test/unit/org/openstreetmap/josm/testutils/ThrowableRootCauseMatcher.java	(revision 10397)
+++ trunk/test/unit/org/openstreetmap/josm/testutils/ThrowableRootCauseMatcher.java	(revision 10397)
@@ -0,0 +1,60 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.testutils;
+
+import org.hamcrest.Description;
+import org.hamcrest.Factory;
+import org.hamcrest.Matcher;
+import org.hamcrest.TypeSafeMatcher;
+
+/**
+ * Matches the root cause of a {@code Throwable}.
+ * This has been rejected from JUnit developers (see https://github.com/junit-team/junit4/pull/778),
+ * But this is really useful, thanks to pimterry for implementing it.
+ *
+ * @param <T> Throwable type
+ * @see <a href="https://github.com/junit-team/junit4/pull/778">Github pull request</a>
+ */
+public class ThrowableRootCauseMatcher<T extends Throwable> extends TypeSafeMatcher<T> {
+
+    private final Matcher<T> fMatcher;
+
+    /**
+     * Constructs a new {@code ThrowableRootCauseMatcher}.
+     * @param matcher matcher
+     */
+    public ThrowableRootCauseMatcher(Matcher<T> matcher) {
+        fMatcher = matcher;
+    }
+
+    @Override
+    public void describeTo(Description description) {
+        description.appendText("exception with cause ");
+        description.appendDescriptionOf(fMatcher);
+    }
+
+    @Override
+    protected boolean matchesSafely(T item) {
+        Throwable exception = item;
+        while (exception.getCause() != null) {
+            exception = exception.getCause();
+        }
+        return fMatcher.matches(exception);
+    }
+
+    @Override
+    protected void describeMismatchSafely(T item, Description description) {
+        description.appendText("cause ");
+        fMatcher.describeMismatch(item.getCause(), description);
+    }
+
+    /**
+     * Returns a new {@code ThrowableRootCauseMatcher} instance.
+     * @param <T> Throwable type
+     * @param matcher matcher
+     * @return new {@code ThrowableRootCauseMatcher} instance
+     */
+    @Factory
+    public static <T extends Throwable> Matcher<T> hasRootCause(final Matcher<T> matcher) {
+        return new ThrowableRootCauseMatcher<>(matcher);
+    }
+}
