Index: /trunk/test/unit/org/openstreetmap/josm/gui/MainApplicationTest.java
===================================================================
--- /trunk/test/unit/org/openstreetmap/josm/gui/MainApplicationTest.java	(revision 19208)
+++ /trunk/test/unit/org/openstreetmap/josm/gui/MainApplicationTest.java	(revision 19209)
@@ -58,4 +58,5 @@
 import org.openstreetmap.josm.testutils.annotations.Main;
 import org.openstreetmap.josm.testutils.annotations.OsmApi;
+import org.openstreetmap.josm.testutils.annotations.Plugins;
 import org.openstreetmap.josm.testutils.annotations.Projection;
 import org.openstreetmap.josm.tools.Logging;
@@ -171,4 +172,5 @@
      * @throws PluginListParseException if an error occurs
      */
+    @Plugins
     @Test
     void testUpdateAndLoadPlugins() throws PluginListParseException {
Index: /trunk/test/unit/org/openstreetmap/josm/plugins/PluginHandlerTestIT.java
===================================================================
--- /trunk/test/unit/org/openstreetmap/josm/plugins/PluginHandlerTestIT.java	(revision 19208)
+++ /trunk/test/unit/org/openstreetmap/josm/plugins/PluginHandlerTestIT.java	(revision 19209)
@@ -43,6 +43,8 @@
 import org.openstreetmap.josm.testutils.annotations.HTTPS;
 import org.openstreetmap.josm.testutils.annotations.Main;
+import org.openstreetmap.josm.testutils.annotations.Plugins;
 import org.openstreetmap.josm.testutils.annotations.Projection;
 import org.openstreetmap.josm.testutils.annotations.Territories;
+import org.openstreetmap.josm.testutils.annotations.ThreadSync;
 import org.openstreetmap.josm.tools.Destroyable;
 import org.openstreetmap.josm.tools.Logging;
@@ -58,4 +60,6 @@
 @Territories
 @Timeout(value = 10, unit = TimeUnit.MINUTES)
+@ThreadSync
+@Plugins
 public class PluginHandlerTestIT {
 
Index: /trunk/test/unit/org/openstreetmap/josm/testutils/annotations/Plugins.java
===================================================================
--- /trunk/test/unit/org/openstreetmap/josm/testutils/annotations/Plugins.java	(revision 19209)
+++ /trunk/test/unit/org/openstreetmap/josm/testutils/annotations/Plugins.java	(revision 19209)
@@ -0,0 +1,62 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.testutils.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+import org.junit.jupiter.api.extension.AfterEachCallback;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.junit.jupiter.api.extension.ExtensionContext;
+import org.openstreetmap.josm.io.AbstractReader;
+import org.openstreetmap.josm.io.OsmServerReadPostprocessor;
+import org.openstreetmap.josm.plugins.PluginHandler;
+import org.openstreetmap.josm.plugins.PluginInformation;
+import org.openstreetmap.josm.tools.Destroyable;
+
+/**
+ * Cleanup plugins if they've been loaded
+ */
+@Target({ElementType.TYPE, ElementType.METHOD})
+@Retention(RetentionPolicy.RUNTIME)
+@ExtendWith(Plugins.PluginExtension.class)
+public @interface Plugins {
+    /**
+     * The extension to clean up after plugin installs
+     */
+    class PluginExtension implements AfterEachCallback {
+
+        @Override
+        public void afterEach(ExtensionContext context) throws Exception {
+            // We want to clean up as much as possible using "standard" methods
+            for (PluginInformation plugin : PluginHandler.getPlugins()) {
+                Object root = PluginHandler.getPlugin(plugin.name);
+                if (root instanceof Destroyable) {
+                    ((Destroyable) root).destroy();
+                    PluginHandler.removePlugins(Collections.singletonList(plugin));
+                }
+            }
+            final Field pluginListField = PluginHandler.class.getDeclaredField("pluginList");
+            final Field classLoadersField = PluginHandler.class.getDeclaredField("classLoaders");
+            final Field postprocessorsField = AbstractReader.class.getDeclaredField("postprocessors");
+            org.openstreetmap.josm.tools.ReflectionUtils.setObjectsAccessible(classLoadersField, postprocessorsField,
+                    pluginListField);
+            ((List<?>) pluginListField.get(null)).clear();
+            ((Map<?, ?>) classLoadersField.get(null)).clear();
+            // Needed due to SDS
+            final Object postprocessors = postprocessorsField.get(null);
+            if (postprocessors instanceof Collection) {
+                for (OsmServerReadPostprocessor pp : new ArrayList<>((Collection<OsmServerReadPostprocessor>) postprocessors)) {
+                    AbstractReader.deregisterPostprocessor(pp);
+                }
+            }
+        }
+    }
+}
