Index: test/unit/org/openstreetmap/josm/testutils/JOSMTestRules.java
===================================================================
--- test/unit/org/openstreetmap/josm/testutils/JOSMTestRules.java	(revision 17428)
+++ test/unit/org/openstreetmap/josm/testutils/JOSMTestRules.java	(working copy)
@@ -60,6 +60,8 @@
 import org.openstreetmap.josm.io.OsmTransferCanceledException;
 import org.openstreetmap.josm.spi.preferences.Config;
 import org.openstreetmap.josm.spi.preferences.Setting;
+import org.openstreetmap.josm.testutils.annotations.UseJosmHome;
+import org.openstreetmap.josm.testutils.annotations.UsePreferences;
 import org.openstreetmap.josm.testutils.mockers.EDTAssertionMocker;
 import org.openstreetmap.josm.testutils.mockers.WindowlessMapViewStateMocker;
 import org.openstreetmap.josm.testutils.mockers.WindowlessNavigatableComponentMocker;
@@ -134,7 +136,9 @@
     /**
      * Enable the use of default preferences.
      * @return this instance, for easy chaining
+     * @deprecated Use {@link UsePreferences} instead.
      */
+    @Deprecated
     public JOSMTestRules preferences() {
         josmHome();
         usePreferences = true;
@@ -144,7 +148,9 @@
     /**
      * Set JOSM home to a valid, empty directory.
      * @return this instance, for easy chaining
+     * @deprecated Use {@link UseJosmHome} instead.
      */
+    @Deprecated
     private JOSMTestRules josmHome() {
         josmHome = new TemporaryFolder();
         return this;
@@ -172,7 +178,9 @@
      * Mock this test's assumed JOSM version (as reported by {@link Version}).
      * @param revisionProperties mock contents of JOSM's {@code REVISION} properties file
      * @return this instance, for easy chaining
+     * @deprecated Use {@link OverrideAssumeRevision} instead.
      */
+    @Deprecated
     public JOSMTestRules assumeRevision(final String revisionProperties) {
         this.assumeRevisionString = revisionProperties;
         return this;
@@ -397,8 +405,11 @@
         return this;
     }
 
-    private static class MockVersion extends Version {
-        MockVersion(final String propertiesString) {
+    /*
+     * Mock the JOSM version. This should only be used in JOSM Core test extensions.
+     */
+    public static class MockVersion extends Version {
+        public MockVersion(final String propertiesString) {
             super.initFromRevisionInfo(
                 new ByteArrayInputStream(propertiesString.getBytes(StandardCharsets.UTF_8))
             );
@@ -409,7 +420,7 @@
     public Statement apply(Statement base, Description description) {
         // First process any Override* annotations for per-test overrides.
         // The following only work because "option" methods modify JOSMTestRules in-place
-        final OverrideAssumeRevision overrideAssumeRevision = description.getAnnotation(OverrideAssumeRevision.class);
+        OverrideAssumeRevision overrideAssumeRevision = description.getAnnotation(OverrideAssumeRevision.class);
         if (overrideAssumeRevision != null) {
             this.assumeRevision(overrideAssumeRevision.value());
         }
@@ -790,11 +801,14 @@
     /**
      * Override this test's assumed JOSM version (as reported by {@link Version}).
      * @see JOSMTestRules#assumeRevision(String)
+     * @deprecated Use {@link org.openstreetmap.josm.testutils.annotations.OverrideAssumeRevision}
+     *             when using JUnit 5 Extensions.
      */
     @Documented
     @Retention(RetentionPolicy.RUNTIME)
     @Target(ElementType.METHOD)
-    public @interface OverrideAssumeRevision {
+    @Deprecated
+    public @interface OverrideAssumeRevision  {
         /**
          * Returns overridden assumed JOSM version.
          * @return overridden assumed JOSM version
Index: test/unit/org/openstreetmap/josm/testutils/annotations/BasicPreferences.java
===================================================================
--- test/unit/org/openstreetmap/josm/testutils/annotations/BasicPreferences.java	(nonexistent)
+++ test/unit/org/openstreetmap/josm/testutils/annotations/BasicPreferences.java	(working copy)
@@ -0,0 +1,23 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.testutils.annotations;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.openstreetmap.josm.testutils.JOSMTestRules;
+import org.openstreetmap.josm.testutils.extensions.BasicPreferencesExtension;
+
+/**
+ * Allow tests to use JOSM preferences (see {@link JOSMTestRules#preferences()})
+ * @author Taylor Smock
+ */
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.METHOD, ElementType.TYPE})
+@ExtendWith(BasicPreferencesExtension.class)
+public @interface BasicPreferences {
+}
Index: test/unit/org/openstreetmap/josm/testutils/annotations/OverrideAssumeRevision.java
===================================================================
--- test/unit/org/openstreetmap/josm/testutils/annotations/OverrideAssumeRevision.java	(nonexistent)
+++ test/unit/org/openstreetmap/josm/testutils/annotations/OverrideAssumeRevision.java	(working copy)
@@ -0,0 +1,27 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.testutils.annotations;
+
+import java.lang.annotation.Documented;
+import  java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import  java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import org.openstreetmap.josm.data.Version;
+import org.openstreetmap.josm.testutils.JOSMTestRules;
+
+
+/**
+ * Override this test's assumed JOSM version (as reported by {@link Version}).
+ * @see JOSMTestRules#assumeRevision(String)
+ */
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.METHOD, ElementType.TYPE})
+public @interface OverrideAssumeRevision {
+    /**
+     * Returns overridden assumed JOSM version.
+     * @return overridden assumed JOSM version
+     */
+    String value();
+}
Index: test/unit/org/openstreetmap/josm/testutils/annotations/UseJosmHome.java
===================================================================
--- test/unit/org/openstreetmap/josm/testutils/annotations/UseJosmHome.java	(nonexistent)
+++ test/unit/org/openstreetmap/josm/testutils/annotations/UseJosmHome.java	(working copy)
@@ -0,0 +1,26 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.testutils.annotations;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.openstreetmap.josm.testutils.JOSMTestRules;
+import org.openstreetmap.josm.testutils.extensions.UseJosmHomeExtension;
+
+/**
+ * Use the JOSM home directory. See {@link JOSMTestRules}.
+ * Typically only used by {@link UsePreferences}.
+ *
+ * @author Taylor Smock
+ *
+ */
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.METHOD, ElementType.TYPE})
+@ExtendWith(UseJosmHomeExtension.class)
+public @interface UseJosmHome {
+}
Index: test/unit/org/openstreetmap/josm/testutils/annotations/UsePreferences.java
===================================================================
--- test/unit/org/openstreetmap/josm/testutils/annotations/UsePreferences.java	(nonexistent)
+++ test/unit/org/openstreetmap/josm/testutils/annotations/UsePreferences.java	(working copy)
@@ -0,0 +1,28 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.testutils.annotations;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.openstreetmap.josm.testutils.JOSMTestRules;
+import org.openstreetmap.josm.testutils.extensions.UsePreferencesExtension;
+
+/**
+ * Allow tests to use JOSM preferences (see {@link JOSMTestRules#preferences()})
+ * @see BasicPreferences (often enough for simple tests).
+ *
+ * @author Taylor Smock
+ */
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.METHOD, ElementType.TYPE})
+@UseJosmHome
+@BasicPreferences
+@ExtendWith(UsePreferencesExtension.class)
+public @interface UsePreferences {
+
+}
Index: test/unit/org/openstreetmap/josm/testutils/extensions/BasicPreferencesExtension.java
===================================================================
--- test/unit/org/openstreetmap/josm/testutils/extensions/BasicPreferencesExtension.java	(nonexistent)
+++ test/unit/org/openstreetmap/josm/testutils/extensions/BasicPreferencesExtension.java	(working copy)
@@ -0,0 +1,31 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.testutils.extensions;
+
+import org.junit.jupiter.api.extension.BeforeAllCallback;
+import org.junit.jupiter.api.extension.ExtensionContext;
+import org.junit.jupiter.api.extension.ExtensionContext.Namespace;
+import org.openstreetmap.josm.data.Preferences;
+import org.openstreetmap.josm.data.preferences.JosmBaseDirectories;
+import org.openstreetmap.josm.data.preferences.JosmUrls;
+import org.openstreetmap.josm.spi.preferences.Config;
+import org.openstreetmap.josm.testutils.annotations.BasicPreferences;
+import org.openstreetmap.josm.testutils.annotations.UsePreferences;
+
+/**
+ * Initialize basic preferences. This is often more than enough for basic tests. Use {@link BasicPreferences}.
+ * @see UsePreferences if {@link BasicPreferences} does not work.
+ * @author Taylor Smock
+ *
+ */
+public class BasicPreferencesExtension implements BeforeAllCallback {
+
+    @Override
+    public void beforeAll(ExtensionContext context) throws Exception {
+        Preferences pref = Preferences.main();
+        Config.setPreferencesInstance(pref);
+        Config.setBaseDirectoriesProvider(JosmBaseDirectories.getInstance());
+        Config.setUrlsProvider(JosmUrls.getInstance());
+        context.getStore(Namespace.create(BasicPreferencesExtension.class)).put("preferences", pref);
+    }
+
+}
Index: test/unit/org/openstreetmap/josm/testutils/extensions/OverrideAssumeRevisionExtension.java
===================================================================
--- test/unit/org/openstreetmap/josm/testutils/extensions/OverrideAssumeRevisionExtension.java	(nonexistent)
+++ test/unit/org/openstreetmap/josm/testutils/extensions/OverrideAssumeRevisionExtension.java	(working copy)
@@ -0,0 +1,43 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.testutils.extensions;
+
+import java.util.Optional;
+
+import org.junit.jupiter.api.extension.AfterEachCallback;
+import org.junit.jupiter.api.extension.BeforeEachCallback;
+import org.junit.jupiter.api.extension.ExtensionContext;
+import org.junit.jupiter.api.extension.ExtensionContext.Namespace;
+import org.junit.jupiter.api.extension.ExtensionContext.Store;
+import org.junit.platform.commons.support.AnnotationSupport;
+import org.openstreetmap.josm.TestUtils;
+import org.openstreetmap.josm.data.Version;
+import org.openstreetmap.josm.testutils.JOSMTestRules;
+import org.openstreetmap.josm.testutils.annotations.OverrideAssumeRevision;
+
+/**
+ * Override the JOSM revision information. Use {@link OverrideAssumeRevision} instead of directly using this extension.
+ * @author Taylor Smock
+ *
+ */
+public class OverrideAssumeRevisionExtension implements BeforeEachCallback, AfterEachCallback {
+
+    @Override
+    public void afterEach(ExtensionContext context) throws Exception {
+        Store store = context.getStore(Namespace.create(OverrideAssumeRevisionExtension.class));
+        String originalVersion = store.getOrDefault(store, String.class, null);
+        if (originalVersion != null) {
+            TestUtils.setPrivateStaticField(Version.class, "instance", originalVersion);
+        }
+    }
+
+    @Override
+    public void beforeEach(ExtensionContext context) throws Exception {
+        Optional<OverrideAssumeRevision> annotation = AnnotationSupport.findAnnotation(context.getElement(), OverrideAssumeRevision.class);
+        if (annotation.isPresent()) {
+            Store store = context.getStore(Namespace.create(OverrideAssumeRevisionExtension.class));
+            store.put("originalVersion", Version.getInstance());
+            final Version replacementVersion = new JOSMTestRules.MockVersion(annotation.get().value());
+            TestUtils.setPrivateStaticField(Version.class, "instance", replacementVersion);
+        }
+    }
+}
Index: test/unit/org/openstreetmap/josm/testutils/extensions/UseJosmHomeExtension.java
===================================================================
--- test/unit/org/openstreetmap/josm/testutils/extensions/UseJosmHomeExtension.java	(nonexistent)
+++ test/unit/org/openstreetmap/josm/testutils/extensions/UseJosmHomeExtension.java	(working copy)
@@ -0,0 +1,50 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.testutils.extensions;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.FileVisitResult;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.SimpleFileVisitor;
+import java.nio.file.attribute.BasicFileAttributes;
+
+import org.junit.jupiter.api.extension.AfterAllCallback;
+import org.junit.jupiter.api.extension.BeforeAllCallback;
+import org.junit.jupiter.api.extension.ExtensionContext;
+import org.openstreetmap.josm.data.preferences.JosmBaseDirectories;
+import org.openstreetmap.josm.testutils.annotations.UseJosmHome;
+
+/**
+ * Create a JOSM home directory. Prefer using {@link UseJosmHome}.
+ * @author Taylor Smock
+ */
+public class UseJosmHomeExtension implements BeforeAllCallback, AfterAllCallback {
+
+    @Override
+    public void afterAll(ExtensionContext context) throws Exception {
+        Path tempDir = Files.createTempDirectory(context.getUniqueId());
+        Files.walkFileTree(tempDir, new SimpleFileVisitor<Path>() {
+            @Override
+            public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
+                Files.delete(dir);
+                return FileVisitResult.CONTINUE;
+            }
+
+            @Override
+            public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
+                Files.delete(file);
+                return FileVisitResult.CONTINUE;
+            }
+        });
+    }
+
+    @Override
+    public void beforeAll(ExtensionContext context) throws Exception {
+        Path tempDir = Files.createTempDirectory(context.getUniqueId());
+        File home = tempDir.toFile();
+        System.setProperty("josm.home", home.getAbsolutePath());
+        JosmBaseDirectories.getInstance().clearMemos();
+    }
+
+}
Index: test/unit/org/openstreetmap/josm/testutils/extensions/UsePreferencesExtension.java
===================================================================
--- test/unit/org/openstreetmap/josm/testutils/extensions/UsePreferencesExtension.java	(nonexistent)
+++ test/unit/org/openstreetmap/josm/testutils/extensions/UsePreferencesExtension.java	(working copy)
@@ -0,0 +1,33 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.testutils.extensions;
+
+import java.util.Map;
+
+import org.junit.jupiter.api.extension.BeforeEachCallback;
+import org.junit.jupiter.api.extension.ExtensionContext;
+import org.junit.jupiter.api.extension.ExtensionContext.Namespace;
+import org.openstreetmap.josm.TestUtils;
+import org.openstreetmap.josm.data.Preferences;
+import org.openstreetmap.josm.spi.preferences.Config;
+import org.openstreetmap.josm.spi.preferences.Setting;
+import org.openstreetmap.josm.testutils.annotations.UsePreferences;
+/**
+ * Initialize preferences. Prefer {@link UsePreferences}.
+ * @author Taylor Smock
+ *
+ */
+public class UsePreferencesExtension implements BeforeEachCallback {
+    @Override
+    public void beforeEach(ExtensionContext context) throws Exception {
+        Preferences pref = context.getStore(Namespace.create(BasicPreferencesExtension.class)).get("preferences", Preferences.class);
+        @SuppressWarnings("unchecked")
+        final Map<String, Setting<?>> defaultsMap = (Map<String, Setting<?>>) TestUtils.getPrivateField(pref, "defaultsMap");
+        defaultsMap.clear();
+        pref.resetToInitialState();
+        pref.enableSaveOnPut(false);
+        // No pref init -> that would only create the preferences file.
+        // We force the use of a wrong API server, just in case anyone attempts an upload
+        Config.getPref().put("osm-server.url", "http://invalid");
+    }
+
+}
