diff --git a/test/functional/org/openstreetmap/josm/tools/HttpClientTest.java b/test/functional/org/openstreetmap/josm/tools/HttpClientTest.java
index 208b05f1f8..bdb3b278de 100644
--- a/test/functional/org/openstreetmap/josm/tools/HttpClientTest.java
+++ b/test/functional/org/openstreetmap/josm/tools/HttpClientTest.java
@@ -15,8 +15,9 @@ import static org.hamcrest.CoreMatchers.nullValue;
 import static org.hamcrest.CoreMatchers.startsWith;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.text.IsEqualIgnoringCase.equalToIgnoringCase;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
 
 import java.io.BufferedReader;
 import java.io.IOException;
@@ -33,40 +34,35 @@ import java.util.logging.LogRecord;
 import java.util.regex.Matcher;
 import java.util.stream.Collectors;
 
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.Timeout;
 import org.openstreetmap.josm.TestUtils;
 import org.openstreetmap.josm.data.Version;
 import org.openstreetmap.josm.gui.progress.ProgressMonitor;
-import org.openstreetmap.josm.testutils.JOSMTestRules;
+import org.openstreetmap.josm.testutils.annotations.BasicPreferences;
+import org.openstreetmap.josm.testutils.annotations.BasicWiremock;
+import org.openstreetmap.josm.testutils.annotations.HTTP;
 import org.openstreetmap.josm.tools.HttpClient.Response;
 
-import com.github.tomakehurst.wiremock.core.WireMockConfiguration;
+import com.github.tomakehurst.wiremock.WireMockServer;
 import com.github.tomakehurst.wiremock.http.HttpHeader;
 import com.github.tomakehurst.wiremock.http.HttpHeaders;
-import com.github.tomakehurst.wiremock.junit.WireMockRule;
 import com.github.tomakehurst.wiremock.matching.UrlPattern;
 
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
-
 /**
  * Tests the {@link HttpClient}.
  */
-public class HttpClientTest {
-
-    /**
-     * Setup test
-     */
-    @Rule
-    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
-    public JOSMTestRules test = new JOSMTestRules().preferences().timeout(15000);
-
+@HTTP
+@BasicWiremock
+@BasicPreferences
+@Timeout(15)
+class HttpClientTest {
     /**
      * mocked local http server
      */
-    @Rule
-    public WireMockRule localServer = new WireMockRule(WireMockConfiguration.options().dynamicPort());
+    @BasicWiremock
+    public WireMockServer localServer;
 
     private ProgressMonitor progress;
 
@@ -90,7 +86,7 @@ public class HttpClientTest {
     /**
      * Setup test.
      */
-    @Before
+    @BeforeEach
     public void setUp() {
         localServer.resetAll();
         progress = TestUtils.newTestProgressMonitor();
@@ -104,7 +100,7 @@ public class HttpClientTest {
      * @throws IOException if an I/O error occurs
      */
     @Test
-    public void testConstructorGetterSetter() throws IOException {
+    void testConstructorGetterSetter() throws IOException {
         final URL localUrl = url("");
         final HttpClient client = HttpClient.create(localUrl);
         assertThat(client.getURL(), is(localUrl));
@@ -126,7 +122,7 @@ public class HttpClientTest {
      * @throws IOException if an I/O error occurs
      */
     @Test
-    public void testGet() throws IOException {
+    void testGet() throws IOException {
         final UrlPattern pattern = urlEqualTo("/get?foo=bar");
         localServer.stubFor(get(pattern).willReturn(aResponse().withStatusMessage("OK")
                 .withHeader("Content-Type", "application/json; encoding=utf-8")));
@@ -150,7 +146,7 @@ public class HttpClientTest {
      * @throws IOException if an I/O error occurs
      */
     @Test
-    public void testHeaders() throws IOException {
+    void testHeaders() throws IOException {
         final UrlPattern pattern = urlEqualTo("/headers");
         localServer.stubFor(get(pattern).willReturn(aResponse()));
         connect("/headers");
@@ -165,7 +161,7 @@ public class HttpClientTest {
      * @throws IOException if an I/O error occurs
      */
     @Test
-    public void testFetchUtf8Content() throws IOException {
+    void testFetchUtf8Content() throws IOException {
         localServer.stubFor(get(urlEqualTo("/encoding/utf8"))
                 .willReturn(aResponse().withBody("∀x∈ℝ: UTF-8 encoded sample plain-text file")));
         final Response response = connect("/encoding/utf8");
@@ -180,7 +176,7 @@ public class HttpClientTest {
      * @throws IOException if an I/O error occurs
      */
     @Test
-    public void testPost() throws IOException {
+    void testPost() throws IOException {
         final UrlPattern pattern = urlEqualTo("/post");
         localServer.stubFor(post(pattern).willReturn(aResponse()));
         final String text = "Hello World!\nGeetings from JOSM, the Java OpenStreetMap Editor";
@@ -199,7 +195,7 @@ public class HttpClientTest {
      * @throws IOException if an I/O error occurs
      */
     @Test
-    public void testPostZero() throws IOException {
+    void testPostZero() throws IOException {
         final UrlPattern pattern = urlEqualTo("/post");
         localServer.stubFor(post(pattern).willReturn(aResponse()));
         final byte[] bytes = "".getBytes(StandardCharsets.UTF_8);
@@ -214,7 +210,7 @@ public class HttpClientTest {
     }
 
     @Test
-    public void testRelativeRedirects() throws IOException {
+    void testRelativeRedirects() throws IOException {
         mockRedirects(false, 3);
         final Response response = connect("/relative-redirect/3");
         assertThat(response.getResponseCode(), is(200));
@@ -222,7 +218,7 @@ public class HttpClientTest {
     }
 
     @Test
-    public void testAbsoluteRedirects() throws IOException {
+    void testAbsoluteRedirects() throws IOException {
         mockRedirects(true, 3);
         final Response response = connect("/absolute-redirect/3");
         assertThat(response.getResponseCode(), is(200));
@@ -233,10 +229,10 @@ public class HttpClientTest {
      * Test maximum number of redirections.
      * @throws IOException if an I/O error occurs
      */
-    @Test(expected = IOException.class)
-    public void testTooMuchRedirects() throws IOException {
+    @Test
+    void testTooMuchRedirects() throws IOException {
         mockRedirects(false, 3);
-        HttpClient.create(url("/relative-redirect/3")).setMaxRedirects(2).connect(progress);
+        assertThrows(IOException.class, () -> HttpClient.create(url("/relative-redirect/3")).setMaxRedirects(2).connect(progress));
     }
 
     /**
@@ -244,7 +240,7 @@ public class HttpClientTest {
      * @throws IOException if an I/O error occurs
      */
     @Test
-    public void testHttp418() throws IOException {
+    void testHttp418() throws IOException {
         // https://tools.ietf.org/html/rfc2324
         final Response response = doTestHttp(418, "I'm a teapot!", "I'm a teapot!",
                 Collections.singletonMap("X-More-Info", "http://tools.ietf.org/html/rfc2324"));
@@ -256,7 +252,7 @@ public class HttpClientTest {
      * @throws IOException if an I/O error occurs
      */
     @Test
-    public void testHttp401() throws IOException {
+    void testHttp401() throws IOException {
         // https://tools.ietf.org/html/rfc2324
         doTestHttp(401, "UNAUTHORIZED", null);
     }
@@ -266,7 +262,7 @@ public class HttpClientTest {
      * @throws IOException if an I/O error occurs
      */
     @Test
-    public void testHttp402() throws IOException {
+    void testHttp402() throws IOException {
         // https://tools.ietf.org/html/rfc2324
         doTestHttp(402, "PAYMENT REQUIRED", "Fuck you, pay me!");
     }
@@ -276,7 +272,7 @@ public class HttpClientTest {
      * @throws IOException if an I/O error occurs
      */
     @Test
-    public void testHttp403() throws IOException {
+    void testHttp403() throws IOException {
         // https://tools.ietf.org/html/rfc2324
         doTestHttp(403, "FORBIDDEN", null);
     }
@@ -286,7 +282,7 @@ public class HttpClientTest {
      * @throws IOException if an I/O error occurs
      */
     @Test
-    public void testHttp404() throws IOException {
+    void testHttp404() throws IOException {
         // https://tools.ietf.org/html/rfc2324
         doTestHttp(404, "NOT FOUND", null);
     }
@@ -296,7 +292,7 @@ public class HttpClientTest {
      * @throws IOException if an I/O error occurs
      */
     @Test
-    public void testHttp500() throws IOException {
+    void testHttp500() throws IOException {
         // https://tools.ietf.org/html/rfc2324
         doTestHttp(500, "INTERNAL SERVER ERROR", null);
     }
@@ -306,7 +302,7 @@ public class HttpClientTest {
      * @throws IOException if any I/O error occurs
      */
     @Test
-    public void testRequestInTime() throws IOException {
+    void testRequestInTime() throws IOException {
         mockDelay(1);
         final Response response = HttpClient.create(url("/delay/1")).setReadTimeout(2000).connect(progress);
         assertThat(response.getResponseCode(), is(200));
@@ -316,10 +312,10 @@ public class HttpClientTest {
      * Checks that a slow request results in the expected exception if it exceeds the timeout.
      * @throws IOException always
      */
-    @Test(expected = IOException.class)
-    public void testTakesTooLong() throws IOException {
+    @Test
+    void testTakesTooLong() throws IOException {
         mockDelay(1);
-        HttpClient.create(url("/delay/1")).setReadTimeout(500).connect(progress);
+        assertThrows(IOException.class, () -> HttpClient.create(url("/delay/1")).setReadTimeout(500).connect(progress));
     }
 
     /**
@@ -327,7 +323,7 @@ public class HttpClientTest {
      * @throws IOException if any I/O error occurs
      */
     @Test
-    public void testGzip() throws IOException {
+    void testGzip() throws IOException {
         localServer.stubFor(get(urlEqualTo("/gzip")).willReturn(aResponse().withBody("foo")));
         final Response response = connect("/gzip");
         assertThat(response.getResponseCode(), is(200));
@@ -340,7 +336,7 @@ public class HttpClientTest {
      * @throws IOException if any I/O error occurs
      */
     @Test
-    public void testOpenUrlGzip() throws IOException {
+    void testOpenUrlGzip() throws IOException {
         final Path path = Paths.get(TestUtils.getTestDataRoot(), "tracks/tracks.gpx.gz");
         final byte[] gpx = Files.readAllBytes(path);
         localServer.stubFor(get(urlEqualTo("/trace/1613906/data"))
@@ -360,7 +356,7 @@ public class HttpClientTest {
      * @throws IOException if any I/O error occurs
      */
     @Test
-    public void testOpenUrlBzip() throws IOException {
+    void testOpenUrlBzip() throws IOException {
         final Path path = Paths.get(TestUtils.getTestDataRoot(), "tracks/tracks.gpx.bz2");
         final byte[] gpx = Files.readAllBytes(path);
         localServer.stubFor(get(urlEqualTo("/trace/785544/data"))
@@ -380,7 +376,7 @@ public class HttpClientTest {
      * @throws IOException if any I/O error occurs
      */
     @Test
-    public void testOpenUrlBzipAccordingToContentDisposition() throws IOException {
+    void testOpenUrlBzipAccordingToContentDisposition() throws IOException {
         final Path path = Paths.get(TestUtils.getTestDataRoot(), "tracks/tracks.gpx.bz2");
         final byte[] gpx = Files.readAllBytes(path);
         localServer.stubFor(get(urlEqualTo("/trace/1350010/data"))
@@ -401,7 +397,7 @@ public class HttpClientTest {
      * Test that error message sent by Tomcat can be parsed.
      */
     @Test
-    public void testTomcatErrorMessage() {
+    void testTomcatErrorMessage() {
         Matcher m = HttpClient.getTomcatErrorMatcher(
             "<html><head><title>Apache Tomcat/DGFiP - Rapport d''erreur</title><style><!--"+
                 "H1 {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:22px;} "+
diff --git a/test/unit/org/openstreetmap/josm/actions/AddImageryLayerActionTest.java b/test/unit/org/openstreetmap/josm/actions/AddImageryLayerActionTest.java
index 2b6cea224d..fb0ea141fc 100644
--- a/test/unit/org/openstreetmap/josm/actions/AddImageryLayerActionTest.java
+++ b/test/unit/org/openstreetmap/josm/actions/AddImageryLayerActionTest.java
@@ -4,47 +4,49 @@ package org.openstreetmap.josm.actions;
 import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
 import static com.github.tomakehurst.wiremock.client.WireMock.get;
 import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo;
-import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
 
 import java.util.List;
 
-import org.junit.Rule;
-import org.junit.Test;
-import org.openstreetmap.josm.TestUtils;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.RegisterExtension;
 import org.openstreetmap.josm.data.imagery.ImageryInfo;
 import org.openstreetmap.josm.gui.MainApplication;
 import org.openstreetmap.josm.gui.layer.TMSLayer;
 import org.openstreetmap.josm.gui.layer.WMSLayer;
 import org.openstreetmap.josm.testutils.JOSMTestRules;
+import org.openstreetmap.josm.testutils.annotations.BasicPreferences;
+import org.openstreetmap.josm.testutils.annotations.BasicWiremock;
 
-import com.github.tomakehurst.wiremock.junit.WireMockRule;
+import com.github.tomakehurst.wiremock.WireMockServer;
 
 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
 
 /**
  * Unit tests for class {@link AddImageryLayerAction}.
  */
-public final class AddImageryLayerActionTest {
+@BasicWiremock
+@BasicPreferences
+final class AddImageryLayerActionTest {
     /**
      * We need prefs for this. We need platform for actions and the OSM API for checking blacklist.
      */
-    @Rule
+    @RegisterExtension
     @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
-    public JOSMTestRules test = new JOSMTestRules().preferences().fakeAPI();
+    public JOSMTestRules test = new JOSMTestRules().fakeAPI();
 
     /**
      * HTTP mock.
      */
-    @Rule
-    public WireMockRule wireMockRule = new WireMockRule(options().dynamicPort().usingFilesUnderDirectory(TestUtils.getTestDataRoot()));
+    @BasicWiremock
+    WireMockServer wireMockServer;
 
     /**
      * Unit test of {@link AddImageryLayerAction#updateEnabledState}.
      */
     @Test
-    public void testEnabledState() {
+    void testEnabledState() {
         assertTrue(new AddImageryLayerAction(new ImageryInfo("foo")).isEnabled());
         assertTrue(new AddImageryLayerAction(new ImageryInfo("foo_tms", "http://bar", "tms", null, null)).isEnabled());
         assertTrue(new AddImageryLayerAction(new ImageryInfo("foo_bing", "http://bar", "bing", null, null)).isEnabled());
@@ -56,7 +58,7 @@ public final class AddImageryLayerActionTest {
      * Unit test of {@link AddImageryLayerAction#actionPerformed} - Enabled cases for TMS.
      */
     @Test
-    public void testActionPerformedEnabledTms() {
+    void testActionPerformedEnabledTms() {
         assertTrue(MainApplication.getLayerManager().getLayersOfType(TMSLayer.class).isEmpty());
         new AddImageryLayerAction(new ImageryInfo("foo_tms", "http://bar", "tms", null, null)).actionPerformed(null);
         List<TMSLayer> tmsLayers = MainApplication.getLayerManager().getLayersOfType(TMSLayer.class);
@@ -68,20 +70,20 @@ public final class AddImageryLayerActionTest {
      * Unit test of {@link AddImageryLayerAction#actionPerformed} - Enabled cases for WMS.
      */
     @Test
-    public void testActionPerformedEnabledWms() {
-        wireMockRule.stubFor(get(urlEqualTo("/wms?SERVICE=WMS&REQUEST=GetCapabilities&VERSION=1.1.1"))
+    void testActionPerformedEnabledWms() {
+        wireMockServer.stubFor(get(urlEqualTo("/wms?SERVICE=WMS&REQUEST=GetCapabilities&VERSION=1.1.1"))
                 .willReturn(aResponse()
                         .withStatus(200)
                         .withHeader("Content-Type", "text/xml")
                         .withBodyFile("imagery/wms-capabilities.xml")));
-        wireMockRule.stubFor(get(urlEqualTo("/wms?SERVICE=WMS&REQUEST=GetCapabilities"))
+        wireMockServer.stubFor(get(urlEqualTo("/wms?SERVICE=WMS&REQUEST=GetCapabilities"))
                 .willReturn(aResponse()
                         .withStatus(404)));
-        wireMockRule.stubFor(get(urlEqualTo("/wms?SERVICE=WMS&REQUEST=GetCapabilities&VERSION=1.3.0"))
+        wireMockServer.stubFor(get(urlEqualTo("/wms?SERVICE=WMS&REQUEST=GetCapabilities&VERSION=1.3.0"))
                 .willReturn(aResponse()
                         .withStatus(404)));
 
-        new AddImageryLayerAction(new ImageryInfo("localhost", wireMockRule.url("/wms?"),
+        new AddImageryLayerAction(new ImageryInfo("localhost", wireMockServer.url("/wms?"),
                 "wms_endpoint", null, null)).actionPerformed(null);
         List<WMSLayer> wmsLayers = MainApplication.getLayerManager().getLayersOfType(WMSLayer.class);
         assertEquals(1, wmsLayers.size());
@@ -93,7 +95,7 @@ public final class AddImageryLayerActionTest {
      * Unit test of {@link AddImageryLayerAction#actionPerformed} - disabled case.
      */
     @Test
-    public void testActionPerformedDisabled() {
+    void testActionPerformedDisabled() {
         assertTrue(MainApplication.getLayerManager().getLayersOfType(TMSLayer.class).isEmpty());
         try {
             new AddImageryLayerAction(new ImageryInfo("foo")).actionPerformed(null);
diff --git a/test/unit/org/openstreetmap/josm/actions/downloadtasks/AbstractDownloadTaskTestParent.java b/test/unit/org/openstreetmap/josm/actions/downloadtasks/AbstractDownloadTaskTestParent.java
index 7fe95379fc..a7beae44cf 100644
--- a/test/unit/org/openstreetmap/josm/actions/downloadtasks/AbstractDownloadTaskTestParent.java
+++ b/test/unit/org/openstreetmap/josm/actions/downloadtasks/AbstractDownloadTaskTestParent.java
@@ -4,13 +4,12 @@ package org.openstreetmap.josm.actions.downloadtasks;
 import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
 import static com.github.tomakehurst.wiremock.client.WireMock.get;
 import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo;
-import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options;
 
-import org.junit.Rule;
-import org.openstreetmap.josm.TestUtils;
+import org.junit.jupiter.api.extension.RegisterExtension;
 import org.openstreetmap.josm.testutils.JOSMTestRules;
+import org.openstreetmap.josm.testutils.annotations.BasicWiremock;
 
-import com.github.tomakehurst.wiremock.junit.WireMockRule;
+import com.github.tomakehurst.wiremock.WireMockServer;
 
 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
 
@@ -22,15 +21,15 @@ public abstract class AbstractDownloadTaskTestParent {
     /**
      * Setup test.
      */
-    @Rule
+    @RegisterExtension
     @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
-    public JOSMTestRules test = new JOSMTestRules().https();
+    JOSMTestRules test = new JOSMTestRules().https();
 
     /**
      * HTTP mock.
      */
-    @Rule
-    public WireMockRule wireMockRule = new WireMockRule(options().dynamicPort().usingFilesUnderDirectory(TestUtils.getTestDataRoot()));
+    @BasicWiremock
+    WireMockServer wireMockServer;
 
     /**
      * Returns the path to remote test file to download via http.
@@ -52,14 +51,14 @@ public abstract class AbstractDownloadTaskTestParent {
      * @return the http URL to remote test file to download
      */
     protected final String getRemoteFileUrl() {
-        return wireMockRule.url(getRemoteFile());
+        return wireMockServer.url(getRemoteFile());
     }
 
     /**
      * Mock the HTTP server.
      */
     protected final void mockHttp() {
-        wireMockRule.stubFor(get(urlEqualTo("/" + getRemoteFile()))
+        wireMockServer.stubFor(get(urlEqualTo("/" + getRemoteFile()))
                 .willReturn(aResponse()
                     .withStatus(200)
                     .withHeader("Content-Type", getRemoteContentType())
diff --git a/test/unit/org/openstreetmap/josm/actions/downloadtasks/DownloadGpsTaskTest.java b/test/unit/org/openstreetmap/josm/actions/downloadtasks/DownloadGpsTaskTest.java
index 48c9597546..6dd3b19c05 100644
--- a/test/unit/org/openstreetmap/josm/actions/downloadtasks/DownloadGpsTaskTest.java
+++ b/test/unit/org/openstreetmap/josm/actions/downloadtasks/DownloadGpsTaskTest.java
@@ -1,25 +1,27 @@
 // License: GPL. For details, see LICENSE file.
 package org.openstreetmap.josm.actions.downloadtasks;
 
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
 
 import java.util.concurrent.ExecutionException;
 
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
 import org.openstreetmap.josm.data.gpx.GpxData;
+import org.openstreetmap.josm.testutils.annotations.BasicWiremock;
 
 /**
  * Unit tests for class {@link DownloadGpsTask}.
  */
-public class DownloadGpsTaskTest extends AbstractDownloadTaskTestParent {
+@BasicWiremock
+class DownloadGpsTaskTest extends AbstractDownloadTaskTestParent {
 
     /**
      * Unit test of {@code DownloadGpsTask#acceptsUrl} method.
      */
     @Test
-    public void testAcceptsURL() {
+    void testAcceptsURL() {
         DownloadGpsTask task = new DownloadGpsTask();
         assertFalse(task.acceptsUrl(null));
         assertFalse(task.acceptsUrl(""));
@@ -41,7 +43,7 @@ public class DownloadGpsTaskTest extends AbstractDownloadTaskTestParent {
      * @throws InterruptedException if the current thread was interrupted while waiting
      */
     @Test
-    public void testDownloadExternalFile() throws InterruptedException, ExecutionException {
+    void testDownloadExternalFile() throws InterruptedException, ExecutionException {
         mockHttp();
         DownloadGpsTask task = new DownloadGpsTask();
         task.loadUrl(new DownloadParams(), getRemoteFileUrl(), null).get();
diff --git a/test/unit/org/openstreetmap/josm/actions/downloadtasks/DownloadNotesTaskTest.java b/test/unit/org/openstreetmap/josm/actions/downloadtasks/DownloadNotesTaskTest.java
index bcfc4c2b26..929fd3fa1a 100644
--- a/test/unit/org/openstreetmap/josm/actions/downloadtasks/DownloadNotesTaskTest.java
+++ b/test/unit/org/openstreetmap/josm/actions/downloadtasks/DownloadNotesTaskTest.java
@@ -1,25 +1,27 @@
 // License: GPL. For details, see LICENSE file.
 package org.openstreetmap.josm.actions.downloadtasks;
 
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
 
 import java.util.concurrent.ExecutionException;
 
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
 import org.openstreetmap.josm.data.osm.NoteData;
+import org.openstreetmap.josm.testutils.annotations.BasicWiremock;
 
 /**
  * Unit tests for class {@link DownloadNotesTask}.
  */
-public class DownloadNotesTaskTest extends AbstractDownloadTaskTestParent {
+@BasicWiremock
+class DownloadNotesTaskTest extends AbstractDownloadTaskTestParent {
 
     /**
      * Unit test of {@code DownloadNotesTask#acceptsUrl} method.
      */
     @Test
-    public void testAcceptsURL() {
+    void testAcceptsURL() {
         DownloadNotesTask task = new DownloadNotesTask();
         assertFalse(task.acceptsUrl(null));
         assertFalse(task.acceptsUrl(""));
@@ -36,7 +38,7 @@ public class DownloadNotesTaskTest extends AbstractDownloadTaskTestParent {
      * @throws InterruptedException if the current thread was interrupted while waiting
      */
     @Test
-    public void testDownloadExternalFile() throws InterruptedException, ExecutionException {
+    void testDownloadExternalFile() throws InterruptedException, ExecutionException {
         mockHttp();
         DownloadNotesTask task = new DownloadNotesTask();
         task.loadUrl(new DownloadParams(), getRemoteFileUrl(), null).get();
diff --git a/test/unit/org/openstreetmap/josm/actions/downloadtasks/DownloadOsmTaskTest.java b/test/unit/org/openstreetmap/josm/actions/downloadtasks/DownloadOsmTaskTest.java
index eae82b3361..d5c514c34a 100644
--- a/test/unit/org/openstreetmap/josm/actions/downloadtasks/DownloadOsmTaskTest.java
+++ b/test/unit/org/openstreetmap/josm/actions/downloadtasks/DownloadOsmTaskTest.java
@@ -1,25 +1,27 @@
 // License: GPL. For details, see LICENSE file.
 package org.openstreetmap.josm.actions.downloadtasks;
 
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
 
 import java.util.concurrent.ExecutionException;
 
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
 import org.openstreetmap.josm.data.osm.DataSet;
+import org.openstreetmap.josm.testutils.annotations.BasicWiremock;
 
 /**
  * Unit tests for class {@link DownloadOsmTask}.
  */
-public class DownloadOsmTaskTest extends AbstractDownloadTaskTestParent {
+@BasicWiremock
+class DownloadOsmTaskTest extends AbstractDownloadTaskTestParent {
 
     /**
      * Unit test of {@code DownloadOsmTask#acceptsUrl} method.
      */
     @Test
-    public void testAcceptsURL() {
+    void testAcceptsURL() {
         DownloadOsmTask task = new DownloadOsmTask();
         assertFalse(task.acceptsUrl(null));
         assertFalse(task.acceptsUrl(""));
@@ -38,7 +40,7 @@ public class DownloadOsmTaskTest extends AbstractDownloadTaskTestParent {
      * @throws InterruptedException if the current thread was interrupted while waiting
      */
     @Test
-    public void testDownloadExternalFile() throws InterruptedException, ExecutionException {
+    void testDownloadExternalFile() throws InterruptedException, ExecutionException {
         mockHttp();
         DownloadOsmTask task = new DownloadOsmTask();
         task.loadUrl(new DownloadParams(), getRemoteFileUrl(), null).get();
diff --git a/test/unit/org/openstreetmap/josm/actions/downloadtasks/PluginDownloadTaskTest.java b/test/unit/org/openstreetmap/josm/actions/downloadtasks/PluginDownloadTaskTest.java
index 29cf44f26f..1407387e92 100644
--- a/test/unit/org/openstreetmap/josm/actions/downloadtasks/PluginDownloadTaskTest.java
+++ b/test/unit/org/openstreetmap/josm/actions/downloadtasks/PluginDownloadTaskTest.java
@@ -1,9 +1,9 @@
 // License: GPL. For details, see LICENSE file.
 package org.openstreetmap.josm.actions.downloadtasks;
 
-import static org.junit.Assert.assertArrayEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
+import static org.junit.jupiter.api.Assertions.assertArrayEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
 
 import java.io.File;
 import java.io.FileInputStream;
@@ -11,14 +11,16 @@ import java.io.FileOutputStream;
 import java.nio.charset.StandardCharsets;
 import java.util.Collections;
 
-import org.junit.Rule;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.RegisterExtension;
 import org.openstreetmap.josm.TestUtils;
 import org.openstreetmap.josm.data.Preferences;
 import org.openstreetmap.josm.gui.progress.NullProgressMonitor;
 import org.openstreetmap.josm.plugins.PluginDownloadTask;
 import org.openstreetmap.josm.plugins.PluginInformation;
 import org.openstreetmap.josm.testutils.JOSMTestRules;
+import org.openstreetmap.josm.testutils.annotations.BasicPreferences;
+import org.openstreetmap.josm.testutils.annotations.BasicWiremock;
 import org.openstreetmap.josm.tools.Utils;
 
 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
@@ -26,17 +28,19 @@ import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
 /**
  * Unit tests for class {@link PluginDownloadTask}.
  */
-public class PluginDownloadTaskTest extends AbstractDownloadTaskTestParent {
+@BasicWiremock
+@BasicPreferences
+class PluginDownloadTaskTest extends AbstractDownloadTaskTestParent {
     protected String pluginPath;
 
     /**
      * Setup test.
      */
-    @Rule
+    @RegisterExtension
     @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
     public JOSMTestRules testRule = new JOSMTestRules().https().assumeRevision(
         "Revision: 8000\n"
-    ).preferences();
+    );
 
     @Override
     protected String getRemoteContentType() {
@@ -54,7 +58,7 @@ public class PluginDownloadTaskTest extends AbstractDownloadTaskTestParent {
      * @throws Exception if an error occurs
      */
     @Test
-    public void testUpdatePluginValid() throws Exception {
+    void testUpdatePluginValid() throws Exception {
         this.pluginPath = "plugin/dummy_plugin.v31772.jar";
         this.mockHttp();
 
@@ -96,7 +100,7 @@ public class PluginDownloadTaskTest extends AbstractDownloadTaskTestParent {
      * @throws Exception if an error occurs
      */
     @Test
-    public void testUpdatePluginCorrupt() throws Exception {
+    void testUpdatePluginCorrupt() throws Exception {
         this.pluginPath = "plugin/corrupted_plugin.jar";
         this.mockHttp();
 
diff --git a/test/unit/org/openstreetmap/josm/data/cache/JCSCachedTileLoaderJobTest.java b/test/unit/org/openstreetmap/josm/data/cache/JCSCachedTileLoaderJobTest.java
index 91ac22d209..ba5aff2bae 100644
--- a/test/unit/org/openstreetmap/josm/data/cache/JCSCachedTileLoaderJobTest.java
+++ b/test/unit/org/openstreetmap/josm/data/cache/JCSCachedTileLoaderJobTest.java
@@ -9,10 +9,10 @@ import static com.github.tomakehurst.wiremock.client.WireMock.head;
 import static com.github.tomakehurst.wiremock.client.WireMock.headRequestedFor;
 import static com.github.tomakehurst.wiremock.client.WireMock.status;
 import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo;
-import static org.junit.Assert.assertArrayEquals;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
+import static org.junit.jupiter.api.Assertions.assertArrayEquals;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
 
 import java.io.IOException;
 import java.net.MalformedURLException;
@@ -22,32 +22,32 @@ import java.util.concurrent.TimeUnit;
 
 import org.apache.commons.jcs3.access.behavior.ICacheAccess;
 import org.apache.commons.jcs3.engine.behavior.ICacheElement;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.Timeout;
 import org.openstreetmap.josm.TestUtils;
 import org.openstreetmap.josm.data.cache.ICachedLoaderListener.LoadResult;
 import org.openstreetmap.josm.data.imagery.TileJobOptions;
-import org.openstreetmap.josm.testutils.JOSMTestRules;
+import org.openstreetmap.josm.testutils.annotations.BasicPreferences;
+import org.openstreetmap.josm.testutils.annotations.BasicWiremock;
 import org.openstreetmap.josm.tools.Logging;
 
-import com.github.tomakehurst.wiremock.core.WireMockConfiguration;
-import com.github.tomakehurst.wiremock.junit.WireMockRule;
+import com.github.tomakehurst.wiremock.WireMockServer;
 import com.github.tomakehurst.wiremock.matching.UrlPattern;
 
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
-
 /**
  * Unit tests for class {@link JCSCachedTileLoaderJob}.
  */
-public class JCSCachedTileLoaderJobTest {
+@BasicWiremock
+@BasicPreferences
+@Timeout(20)
+class JCSCachedTileLoaderJobTest {
 
     /**
      * mocked tile server
      */
-    @Rule
-    public WireMockRule tileServer = new WireMockRule(WireMockConfiguration.options()
-            .dynamicPort());
+    @BasicWiremock
+    WireMockServer tileServer;
 
     private static class TestCachedTileLoaderJob extends JCSCachedTileLoaderJob<String, CacheEntry> {
         private final String url;
@@ -102,19 +102,12 @@ public class JCSCachedTileLoaderJobTest {
         }
     }
 
-    /**
-     * Setup test.
-     */
-    @Rule
-    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
-    public JOSMTestRules test = new JOSMTestRules().preferences().timeout(20_000);
-
     /**
      * Always clear cache before tests
      * @throws Exception when clearing fails
      */
-    @Before
-    public void clearCache() throws Exception {
+    @BeforeEach
+    void clearCache() throws Exception {
         getCache().clear();
     }
 
@@ -124,7 +117,7 @@ public class JCSCachedTileLoaderJobTest {
      * @throws IOException in case of I/O error
      */
     @Test
-    public void testStatusCodes() throws IOException, InterruptedException {
+    void testStatusCodes() throws IOException, InterruptedException {
         doTestStatusCode(200);
         doTestStatusCode(401);
         doTestStatusCode(402);
@@ -141,7 +134,7 @@ public class JCSCachedTileLoaderJobTest {
      * @throws IOException in case of I/O error
      */
     @Test
-    public void testUnknownHost() throws IOException {
+    void testUnknownHost() throws IOException {
         String key = "key_unknown_host";
         TestCachedTileLoaderJob job = new TestCachedTileLoaderJob("http://unkownhost.unkownhost/unkown", key);
         Listener listener = submitJob(job);
@@ -192,7 +185,7 @@ public class JCSCachedTileLoaderJobTest {
      * @throws IOException exception
      */
     @Test
-    public void testNoRequestMadeWhenEntryInCache() throws IOException {
+    void testNoRequestMadeWhenEntryInCache() throws IOException {
         ICacheAccess<String, CacheEntry> cache = getCache();
         long expires = TimeUnit.DAYS.toMillis(1);
         long testStart = System.currentTimeMillis();
@@ -213,7 +206,7 @@ public class JCSCachedTileLoaderJobTest {
      * @throws IOException exception
      */
     @Test
-    public void testRequestMadeWhenEntryInCacheAndForce() throws IOException {
+    void testRequestMadeWhenEntryInCacheAndForce() throws IOException {
         ICacheAccess<String, CacheEntry> cache = getCache();
         long expires = TimeUnit.DAYS.toMillis(1);
         long testStart = System.currentTimeMillis();
@@ -235,7 +228,7 @@ public class JCSCachedTileLoaderJobTest {
      * @throws IOException exception
      */
     @Test
-    public void testSettingMinimumExpiryWhenNoExpires() throws IOException {
+    void testSettingMinimumExpiryWhenNoExpires() throws IOException {
         long testStart = System.currentTimeMillis();
         tileServer.stubFor(get(urlEqualTo("/test")).willReturn(aResponse().withBody("mock entry")));
 
@@ -243,15 +236,15 @@ public class JCSCachedTileLoaderJobTest {
         Listener listener = submitJob(job, false);
         tileServer.verify(1, getRequestedFor(urlEqualTo("/test")));
 
-        assertTrue("Cache entry expiration is " + (listener.attributes.getExpirationTime() - testStart) + " which is not larger than " +
-                JCSCachedTileLoaderJob.DEFAULT_EXPIRE_TIME + " (DEFAULT_EXPIRE_TIME)",
-                listener.attributes.getExpirationTime() >= testStart + JCSCachedTileLoaderJob.DEFAULT_EXPIRE_TIME);
+        assertTrue(listener.attributes.getExpirationTime() >= testStart + JCSCachedTileLoaderJob.DEFAULT_EXPIRE_TIME,
+                "Cache entry expiration is " + (listener.attributes.getExpirationTime() - testStart) + " which is not larger than " +
+                        JCSCachedTileLoaderJob.DEFAULT_EXPIRE_TIME + " (DEFAULT_EXPIRE_TIME)");
 
-        assertTrue("Cache entry expiration is " +
-                (listener.attributes.getExpirationTime() - System.currentTimeMillis()) +
-                " which is not less than " +
-                JCSCachedTileLoaderJob.DEFAULT_EXPIRE_TIME + " (DEFAULT_EXPIRE_TIME)",
-                listener.attributes.getExpirationTime() <= System.currentTimeMillis() + JCSCachedTileLoaderJob.DEFAULT_EXPIRE_TIME
+        assertTrue(listener.attributes.getExpirationTime() <= System.currentTimeMillis() + JCSCachedTileLoaderJob.DEFAULT_EXPIRE_TIME,
+                "Cache entry expiration is " +
+                        (listener.attributes.getExpirationTime() - System.currentTimeMillis()) +
+                        " which is not less than " +
+                        JCSCachedTileLoaderJob.DEFAULT_EXPIRE_TIME + " (DEFAULT_EXPIRE_TIME)"
                 );
 
         assertArrayEquals("mock entry".getBytes(StandardCharsets.UTF_8), listener.data);
@@ -263,7 +256,7 @@ public class JCSCachedTileLoaderJobTest {
      * @throws IOException exception
      */
     @Test
-    public void testSettingExpireByMaxAge() throws IOException {
+    void testSettingExpireByMaxAge() throws IOException {
         long testStart = System.currentTimeMillis();
         long expires = TimeUnit.DAYS.toSeconds(1);
         tileServer.stubFor(get(urlEqualTo("/test"))
@@ -277,16 +270,17 @@ public class JCSCachedTileLoaderJobTest {
         Listener listener = submitJob(job, false);
         tileServer.verify(1, getRequestedFor(urlEqualTo("/test")));
 
-        assertTrue("Cache entry expiration is " + (listener.attributes.getExpirationTime() - testStart) + " which is not larger than " +
-                TimeUnit.SECONDS.toMillis(expires) + " (max-age)",
-                listener.attributes.getExpirationTime() >= testStart + TimeUnit.SECONDS.toMillis(expires));
+        assertTrue(listener.attributes.getExpirationTime() >= testStart + TimeUnit.SECONDS.toMillis(expires),
+                "Cache entry expiration is " + (listener.attributes.getExpirationTime() - testStart) + " which is not larger than " +
+                        TimeUnit.SECONDS.toMillis(expires) + " (max-age)");
 
-        assertTrue("Cache entry expiration is " +
-                (listener.attributes.getExpirationTime() - System.currentTimeMillis()) +
-                " which is not less than " +
-                TimeUnit.SECONDS.toMillis(expires) + " (max-age)",
-                listener.attributes.getExpirationTime() <= System.currentTimeMillis() + TimeUnit.SECONDS.toMillis(expires)
-                );
+        assertTrue(
+                listener.attributes.getExpirationTime() <= System.currentTimeMillis() + TimeUnit.SECONDS.toMillis(expires),
+                "Cache entry expiration is " +
+                        (listener.attributes.getExpirationTime() - System.currentTimeMillis()) +
+                        " which is not less than " +
+                        TimeUnit.SECONDS.toMillis(expires) + " (max-age)"
+                        );
 
         assertArrayEquals("mock entry".getBytes(StandardCharsets.UTF_8), listener.data);
     }
@@ -297,7 +291,7 @@ public class JCSCachedTileLoaderJobTest {
      * @throws IOException exception
      */
     @Test
-    public void testSettingMinimumExpiryByMinimumExpiryTimeLessThanDefault() throws IOException {
+    void testSettingMinimumExpiryByMinimumExpiryTimeLessThanDefault() throws IOException {
         long testStart = System.currentTimeMillis();
         int minimumExpiryTimeSeconds = (int) (JCSCachedTileLoaderJob.DEFAULT_EXPIRE_TIME / 2);
 
@@ -309,16 +303,18 @@ public class JCSCachedTileLoaderJobTest {
         assertArrayEquals("mock entry".getBytes(StandardCharsets.UTF_8), listener.data);
 
 
-        assertTrue("Cache entry expiration is " + (listener.attributes.getExpirationTime() - testStart) + " which is not larger than " +
-                TimeUnit.SECONDS.toMillis(minimumExpiryTimeSeconds) + " (minimumExpireTime)",
-                listener.attributes.getExpirationTime() >= testStart + TimeUnit.SECONDS.toMillis(minimumExpiryTimeSeconds));
+        assertTrue(
+                listener.attributes.getExpirationTime() >= testStart + TimeUnit.SECONDS.toMillis(minimumExpiryTimeSeconds),
+                "Cache entry expiration is " + (listener.attributes.getExpirationTime() - testStart) + " which is not larger than " +
+                        TimeUnit.SECONDS.toMillis(minimumExpiryTimeSeconds) + " (minimumExpireTime)");
 
-        assertTrue("Cache entry expiration is " +
-                (listener.attributes.getExpirationTime() - System.currentTimeMillis()) +
-                " which is not less than " +
-                TimeUnit.SECONDS.toMillis(minimumExpiryTimeSeconds) + " (minimumExpireTime)",
-                listener.attributes.getExpirationTime() <= System.currentTimeMillis() + TimeUnit.SECONDS.toMillis(minimumExpiryTimeSeconds)
-                );
+        assertTrue(
+                listener.attributes.getExpirationTime() <= System.currentTimeMillis() + TimeUnit.SECONDS.toMillis(minimumExpiryTimeSeconds),
+                "Cache entry expiration is " +
+                        (listener.attributes.getExpirationTime() - System.currentTimeMillis()) +
+                        " which is not less than " +
+                        TimeUnit.SECONDS.toMillis(minimumExpiryTimeSeconds) + " (minimumExpireTime)"
+                        );
     }
 
     /**
@@ -328,7 +324,7 @@ public class JCSCachedTileLoaderJobTest {
      */
 
     @Test
-    public void testSettingMinimumExpiryByMinimumExpiryTimeGreaterThanDefault() throws IOException {
+    void testSettingMinimumExpiryByMinimumExpiryTimeGreaterThanDefault() throws IOException {
         long testStart = System.currentTimeMillis();
         int minimumExpiryTimeSeconds = (int) (JCSCachedTileLoaderJob.DEFAULT_EXPIRE_TIME * 2);
 
@@ -340,16 +336,18 @@ public class JCSCachedTileLoaderJobTest {
         assertArrayEquals("mock entry".getBytes(StandardCharsets.UTF_8), listener.data);
 
 
-        assertTrue("Cache entry expiration is " + (listener.attributes.getExpirationTime() - testStart) + " which is not larger than " +
-                TimeUnit.SECONDS.toMillis(minimumExpiryTimeSeconds) + " (minimumExpireTime)",
-                listener.attributes.getExpirationTime() >= testStart + TimeUnit.SECONDS.toMillis(minimumExpiryTimeSeconds));
+        assertTrue(
+                listener.attributes.getExpirationTime() >= testStart + TimeUnit.SECONDS.toMillis(minimumExpiryTimeSeconds),
+                "Cache entry expiration is " + (listener.attributes.getExpirationTime() - testStart) + " which is not larger than " +
+                        TimeUnit.SECONDS.toMillis(minimumExpiryTimeSeconds) + " (minimumExpireTime)");
 
-        assertTrue("Cache entry expiration is " +
-                (listener.attributes.getExpirationTime() - System.currentTimeMillis()) +
-                " which is not less than " +
-                TimeUnit.SECONDS.toMillis(minimumExpiryTimeSeconds) + " (minimumExpireTime)",
-                listener.attributes.getExpirationTime() <= System.currentTimeMillis() + TimeUnit.SECONDS.toMillis(minimumExpiryTimeSeconds)
-                );
+        assertTrue(
+                listener.attributes.getExpirationTime() <= System.currentTimeMillis() + TimeUnit.SECONDS.toMillis(minimumExpiryTimeSeconds),
+                "Cache entry expiration is " +
+                        (listener.attributes.getExpirationTime() - System.currentTimeMillis()) +
+                        " which is not less than " +
+                        TimeUnit.SECONDS.toMillis(minimumExpiryTimeSeconds) + " (minimumExpireTime)"
+                        );
     }
 
     /**
@@ -364,7 +362,7 @@ public class JCSCachedTileLoaderJobTest {
      */
 
     @Test
-    public void testCacheControlVsExpires() throws IOException {
+    void testCacheControlVsExpires() throws IOException {
         long testStart = System.currentTimeMillis();
         int minimumExpiryTimeSeconds = 0;
 
@@ -389,16 +387,17 @@ public class JCSCachedTileLoaderJobTest {
         assertArrayEquals("mock entry".getBytes(StandardCharsets.UTF_8), listener.data);
 
 
-        assertTrue("Cache entry expiration is " + (listener.attributes.getExpirationTime() - testStart) + " which is not larger than " +
-                (JCSCachedTileLoaderJob.DEFAULT_EXPIRE_TIME / 10) + " (Expires header)",
-                listener.attributes.getExpirationTime() >= testStart + (JCSCachedTileLoaderJob.DEFAULT_EXPIRE_TIME / 10));
+        assertTrue(
+                listener.attributes.getExpirationTime() >= testStart + (JCSCachedTileLoaderJob.DEFAULT_EXPIRE_TIME / 10),
+                "Cache entry expiration is " + (listener.attributes.getExpirationTime() - testStart) + " which is not larger than " +
+                        (JCSCachedTileLoaderJob.DEFAULT_EXPIRE_TIME / 10) + " (Expires header)");
 
-        assertTrue("Cache entry expiration is " +
-                (listener.attributes.getExpirationTime() - System.currentTimeMillis()) +
-                " which is not less than " +
-                (JCSCachedTileLoaderJob.DEFAULT_EXPIRE_TIME / 2) + " (Cache-Control: max-age=)",
-                listener.attributes.getExpirationTime() <= System.currentTimeMillis() + (JCSCachedTileLoaderJob.DEFAULT_EXPIRE_TIME / 2)
-                );
+        assertTrue(listener.attributes.getExpirationTime() <= System.currentTimeMillis() + (JCSCachedTileLoaderJob.DEFAULT_EXPIRE_TIME / 2),
+                "Cache entry expiration is " +
+                        (listener.attributes.getExpirationTime() - System.currentTimeMillis()) +
+                        " which is not less than " +
+                        (JCSCachedTileLoaderJob.DEFAULT_EXPIRE_TIME / 2) + " (Cache-Control: max-age=)"
+                        );
     }
 
     /**
@@ -409,7 +408,7 @@ public class JCSCachedTileLoaderJobTest {
      * @throws IOException exception
      */
     @Test
-    public void testMaxAgeVsSMaxAge() throws IOException {
+    void testMaxAgeVsSMaxAge() throws IOException {
         long testStart = System.currentTimeMillis();
         int minimumExpiryTimeSeconds = 0;
 
@@ -434,16 +433,17 @@ public class JCSCachedTileLoaderJobTest {
         tileServer.verify(1, getRequestedFor(urlEqualTo("/test")));
         assertArrayEquals("mock entry".getBytes(StandardCharsets.UTF_8), listener.data);
 
-        assertTrue("Cache entry expiration is " + (listener.attributes.getExpirationTime() - testStart) + " which is not larger than " +
-                (JCSCachedTileLoaderJob.DEFAULT_EXPIRE_TIME / 10) + " (Cache-Control: max-age)",
-                listener.attributes.getExpirationTime() >= testStart + (JCSCachedTileLoaderJob.DEFAULT_EXPIRE_TIME / 10));
-
-        assertTrue("Cache entry expiration is " +
-                (listener.attributes.getExpirationTime() - System.currentTimeMillis()) +
-                " which is not less than " +
-                (JCSCachedTileLoaderJob.DEFAULT_EXPIRE_TIME / 2) + " (Cache-Control: s-max-age)",
-                listener.attributes.getExpirationTime() <= System.currentTimeMillis() + (JCSCachedTileLoaderJob.DEFAULT_EXPIRE_TIME / 2)
-                );
+        assertTrue(
+                listener.attributes.getExpirationTime() >= testStart + (JCSCachedTileLoaderJob.DEFAULT_EXPIRE_TIME / 10),
+                "Cache entry expiration is " + (listener.attributes.getExpirationTime() - testStart) + " which is not larger than " +
+                        (JCSCachedTileLoaderJob.DEFAULT_EXPIRE_TIME / 10) + " (Cache-Control: max-age)");
+
+        assertTrue(listener.attributes.getExpirationTime() <= System.currentTimeMillis() + (JCSCachedTileLoaderJob.DEFAULT_EXPIRE_TIME / 2),
+                "Cache entry expiration is " +
+                        (listener.attributes.getExpirationTime() - System.currentTimeMillis()) +
+                        " which is not less than " +
+                        (JCSCachedTileLoaderJob.DEFAULT_EXPIRE_TIME / 2) + " (Cache-Control: s-max-age)"
+                        );
     }
 
     /**
@@ -451,7 +451,7 @@ public class JCSCachedTileLoaderJobTest {
      * @throws IOException exception
      */
     @Test
-    public void testCheckUsingHead() throws IOException {
+    void testCheckUsingHead() throws IOException {
         ICacheAccess<String, CacheEntry> cache = getCache();
         long expires = TimeUnit.DAYS.toMillis(1);
         long testStart = System.currentTimeMillis();
diff --git a/test/unit/org/openstreetmap/josm/data/imagery/TMSCachedTileLoaderJobTest.java b/test/unit/org/openstreetmap/josm/data/imagery/TMSCachedTileLoaderJobTest.java
index 3438e5a8ff..e42d4d97f8 100644
--- a/test/unit/org/openstreetmap/josm/data/imagery/TMSCachedTileLoaderJobTest.java
+++ b/test/unit/org/openstreetmap/josm/data/imagery/TMSCachedTileLoaderJobTest.java
@@ -1,9 +1,9 @@
 // License: GPL. For details, see LICENSE file.
 package org.openstreetmap.josm.data.imagery;
 
-import static org.junit.Assert.assertArrayEquals;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
+import static org.junit.jupiter.api.Assertions.assertArrayEquals;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
 
 import java.io.IOException;
 import java.net.MalformedURLException;
@@ -16,9 +16,8 @@ import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
 import org.apache.commons.jcs3.access.behavior.ICacheAccess;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
 import org.openstreetmap.gui.jmapviewer.Tile;
 import org.openstreetmap.gui.jmapviewer.interfaces.TileLoaderListener;
 import org.openstreetmap.gui.jmapviewer.tilesources.TMSTileSource;
@@ -26,37 +25,28 @@ import org.openstreetmap.josm.TestUtils;
 import org.openstreetmap.josm.data.cache.BufferedImageCacheEntry;
 import org.openstreetmap.josm.data.cache.CacheEntryAttributes;
 import org.openstreetmap.josm.data.cache.JCSCacheManager;
-import org.openstreetmap.josm.testutils.JOSMTestRules;
+import org.openstreetmap.josm.testutils.annotations.BasicPreferences;
+import org.openstreetmap.josm.testutils.annotations.BasicWiremock;
 import org.openstreetmap.josm.tools.Logging;
 import org.openstreetmap.josm.tools.Utils;
 
+import com.github.tomakehurst.wiremock.WireMockServer;
 import com.github.tomakehurst.wiremock.client.WireMock;
-import com.github.tomakehurst.wiremock.core.WireMockConfiguration;
-import com.github.tomakehurst.wiremock.junit.WireMockRule;
-
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
 
 /**
  * Unit tests for class {@link TMSCachedTileLoaderJob}.
  */
-public class TMSCachedTileLoaderJobTest {
-
-    /**
-     * Setup tests
-     */
-    @Rule
-    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
-    public JOSMTestRules test = new JOSMTestRules().preferences();
-
+@BasicWiremock
+@BasicPreferences
+class TMSCachedTileLoaderJobTest {
     /**
      * mocked tile server
      */
-    @Rule
-    public WireMockRule tileServer = new WireMockRule(WireMockConfiguration.options()
-            .dynamicPort());
+    @BasicWiremock
+    WireMockServer tileServer;
 
-    @Before
-    public void clearCache() throws Exception {
+    @BeforeEach
+    void clearCache() throws Exception {
         getCache().clear();
     }
 
@@ -144,7 +134,7 @@ public class TMSCachedTileLoaderJobTest {
      * Tests that {@code TMSCachedTileLoaderJob#SERVICE_EXCEPTION_PATTERN} is correct.
      */
     @Test
-    public void testServiceExceptionPattern() {
+    void testServiceExceptionPattern() {
         testServiceException("missing parameters ['version', 'format']",
                 "<?xml version=\"1.0\"?>\n" +
                 "<!DOCTYPE ServiceExceptionReport SYSTEM \"http://schemas.opengis.net/wms/1.1.1/exception_1_1_1.dtd\">\n" +
@@ -166,7 +156,7 @@ public class TMSCachedTileLoaderJobTest {
      * Tests that {@code TMSCachedTileLoaderJob#CDATA_PATTERN} is correct.
      */
     @Test
-    public void testCdataPattern() {
+    void testCdataPattern() {
         testCdata("received unsuitable wms request: no <grid> with suitable srs found for layer capitais",
                 "<![CDATA[\r\n" +
                 "received unsuitable wms request: no <grid> with suitable srs found for layer capitais\r\n" +
@@ -177,7 +167,7 @@ public class TMSCachedTileLoaderJobTest {
      * Tests that {@code TMSCachedTileLoaderJob#JSON_PATTERN} is correct.
      */
     @Test
-    public void testJsonPattern() {
+    void testJsonPattern() {
         testJson("Tile does not exist",
                 "{\"message\":\"Tile does not exist\"}");
     }
@@ -196,7 +186,7 @@ public class TMSCachedTileLoaderJobTest {
 
     private static void test(Pattern pattern, String expected, String text) {
         Matcher m = pattern.matcher(text);
-        assertTrue(text, m.matches());
+        assertTrue(m.matches(), text);
         assertEquals(expected, Utils.strip(m.group(1)));
     }
 
@@ -226,7 +216,7 @@ public class TMSCachedTileLoaderJobTest {
      * @throws IOException exception
      */
     @Test
-    public void testNoCacheHeaders() throws IOException {
+    void testNoCacheHeaders() throws IOException {
         long testStart = System.currentTimeMillis();
         tileServer.stubFor(
                 WireMock.get(WireMock.urlEqualTo("/test"))
@@ -249,7 +239,7 @@ public class TMSCachedTileLoaderJobTest {
      * @throws IOException exception
      */
     @Test
-    public void testNoCacheHeadersMinimumExpires() throws IOException {
+    void testNoCacheHeadersMinimumExpires() throws IOException {
         noCacheHeadersMinimumExpires((int) TimeUnit.MILLISECONDS.toSeconds(TMSCachedTileLoaderJob.MINIMUM_EXPIRES.get() * 2));
     }
 
@@ -260,7 +250,7 @@ public class TMSCachedTileLoaderJobTest {
      */
 
     @Test
-    public void testNoCacheHeadersMinimumExpiresLargerThanMaximum() throws IOException {
+    void testNoCacheHeadersMinimumExpiresLargerThanMaximum() throws IOException {
         noCacheHeadersMinimumExpires((int) TimeUnit.MILLISECONDS.toSeconds(TMSCachedTileLoaderJob.MAXIMUM_EXPIRES.get() * 2));
     }
 
@@ -286,7 +276,7 @@ public class TMSCachedTileLoaderJobTest {
      * @throws IOException exception
      */
     @Test
-    public void testShortExpire() throws IOException {
+    void testShortExpire() throws IOException {
         long testStart = System.currentTimeMillis();
         long expires = TMSCachedTileLoaderJob.MINIMUM_EXPIRES.get() / 2;
         tileServer.stubFor(
@@ -306,23 +296,19 @@ public class TMSCachedTileLoaderJobTest {
     }
 
     private void assertExpirationAtLeast(long duration, TestCachedTileLoaderJob job) {
-        assertTrue(
-                "Expiration time shorter by " +
-                        -1 * (job.getAttributes().getExpirationTime() - duration) +
-                        " than expected",
-                job.getAttributes().getExpirationTime() >= duration);
+        assertTrue(job.getAttributes().getExpirationTime() >= duration, "Expiration time shorter by " +
+                                -1 * (job.getAttributes().getExpirationTime() - duration) +
+                                " than expected");
     }
 
     private void assertExpirationAtMost(long duration, TestCachedTileLoaderJob job) {
-        assertTrue(
-                "Expiration time longer by " +
-                        (job.getAttributes().getExpirationTime() - duration) +
-                        " than expected",
-                job.getAttributes().getExpirationTime() <= duration);
+        assertTrue(job.getAttributes().getExpirationTime() <= duration, "Expiration time longer by " +
+                                (job.getAttributes().getExpirationTime() - duration) +
+                                " than expected");
     }
 
     @Test
-    public void testLongExpire() throws IOException {
+    void testLongExpire() throws IOException {
         long testStart = System.currentTimeMillis();
         long expires = TMSCachedTileLoaderJob.MAXIMUM_EXPIRES.get() * 2;
         tileServer.stubFor(
@@ -342,5 +328,4 @@ public class TMSCachedTileLoaderJobTest {
         tileServer.verify(1, WireMock.getRequestedFor(WireMock.urlEqualTo("/test")));
         assertArrayEquals("mock entry".getBytes(StandardCharsets.UTF_8), job.get().getContent());
     }
-
 }
diff --git a/test/unit/org/openstreetmap/josm/data/imagery/WMSEndpointTileSourceTest.java b/test/unit/org/openstreetmap/josm/data/imagery/WMSEndpointTileSourceTest.java
index 31abe4d396..be71d94674 100644
--- a/test/unit/org/openstreetmap/josm/data/imagery/WMSEndpointTileSourceTest.java
+++ b/test/unit/org/openstreetmap/josm/data/imagery/WMSEndpointTileSourceTest.java
@@ -1,42 +1,42 @@
 // License: GPL. For details, see LICENSE file.
 package org.openstreetmap.josm.data.imagery;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
 
 import java.nio.file.Files;
 import java.nio.file.Paths;
 import java.util.Arrays;
 
-import org.junit.Rule;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.RegisterExtension;
 import org.openstreetmap.josm.TestUtils;
 import org.openstreetmap.josm.data.imagery.ImageryInfo.ImageryType;
 import org.openstreetmap.josm.data.projection.ProjectionRegistry;
 import org.openstreetmap.josm.data.projection.Projections;
 import org.openstreetmap.josm.spi.preferences.Config;
 import org.openstreetmap.josm.testutils.JOSMTestRules;
+import org.openstreetmap.josm.testutils.annotations.BasicWiremock;
 
+import com.github.tomakehurst.wiremock.WireMockServer;
 import com.github.tomakehurst.wiremock.client.WireMock;
-import com.github.tomakehurst.wiremock.core.WireMockConfiguration;
-import com.github.tomakehurst.wiremock.junit.WireMockRule;
 
 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
 
-public class WMSEndpointTileSourceTest {
+@BasicWiremock
+class WMSEndpointTileSourceTest {
     /**
      * Setup test
      */
-    @Rule
+    @RegisterExtension
     @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
     public JOSMTestRules test = new JOSMTestRules().projection();
 
-    @Rule
-    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
-    public WireMockRule tileServer = new WireMockRule(WireMockConfiguration.options().dynamicPort());
+    @BasicWiremock
+    WireMockServer tileServer;
 
     @Test
-    public void testDefaultLayerSetInMaps() throws Exception {
+    void testDefaultLayerSetInMaps() throws Exception {
 
         tileServer.stubFor(
                 WireMock.get(WireMock.urlEqualTo("/capabilities?SERVICE=WMS&REQUEST=GetCapabilities"))
@@ -86,7 +86,7 @@ public class WMSEndpointTileSourceTest {
     }
 
     @Test
-    public void testCustomHeaders() throws Exception {
+    void testCustomHeaders() throws Exception {
         tileServer.stubFor(
                 WireMock.get(WireMock.urlEqualTo("/capabilities?SERVICE=WMS&REQUEST=GetCapabilities"))
                 .willReturn(
diff --git a/test/unit/org/openstreetmap/josm/data/imagery/WMTSTileSourceTest.java b/test/unit/org/openstreetmap/josm/data/imagery/WMTSTileSourceTest.java
index ee46b182f4..5cef454908 100644
--- a/test/unit/org/openstreetmap/josm/data/imagery/WMTSTileSourceTest.java
+++ b/test/unit/org/openstreetmap/josm/data/imagery/WMTSTileSourceTest.java
@@ -1,8 +1,8 @@
 // License: GPL. For details, see LICENSE file.
 package org.openstreetmap.josm.data.imagery;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
 
 import java.io.File;
 import java.io.IOException;
@@ -14,10 +14,9 @@ import java.util.Arrays;
 import java.util.List;
 import java.util.concurrent.TimeUnit;
 
-import org.junit.ClassRule;
-import org.junit.Ignore;
-import org.junit.Rule;
-import org.junit.Test;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.RegisterExtension;
 import org.openstreetmap.gui.jmapviewer.TileXY;
 import org.openstreetmap.gui.jmapviewer.tilesources.TemplatedTMSTileSource;
 import org.openstreetmap.josm.TestUtils;
@@ -29,28 +28,30 @@ import org.openstreetmap.josm.data.projection.ProjectionRegistry;
 import org.openstreetmap.josm.data.projection.Projections;
 import org.openstreetmap.josm.spi.preferences.Config;
 import org.openstreetmap.josm.testutils.JOSMTestRules;
+import org.openstreetmap.josm.testutils.annotations.BasicPreferences;
+import org.openstreetmap.josm.testutils.annotations.BasicWiremock;
 
+import com.github.tomakehurst.wiremock.WireMockServer;
 import com.github.tomakehurst.wiremock.client.WireMock;
-import com.github.tomakehurst.wiremock.core.WireMockConfiguration;
-import com.github.tomakehurst.wiremock.junit.WireMockRule;
 
 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
 
 /**
  * Unit tests for class {@link WMTSTileSource}.
  */
-public class WMTSTileSourceTest {
+@BasicWiremock
+@BasicPreferences
+class WMTSTileSourceTest {
 
     /**
      * Setup test.
      */
-    @ClassRule
+    @RegisterExtension
     @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
-    public static JOSMTestRules test = new JOSMTestRules().preferences().projection().timeout((int) TimeUnit.MINUTES.toMillis(5));
+    public static JOSMTestRules test = new JOSMTestRules().projection().timeout((int) TimeUnit.MINUTES.toMillis(5));
 
-    @Rule
-    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
-    public WireMockRule tileServer = new WireMockRule(WireMockConfiguration.options().dynamicPort());
+    @BasicWiremock
+    WireMockServer tileServer;
 
     private final ImageryInfo testImageryTMS = new ImageryInfo("test imagery", "http://localhost", "tms", null, null);
     private final ImageryInfo testImageryPSEUDO_MERCATOR = getImagery(TestUtils.getTestDataRoot() + "wmts/getcapabilities-pseudo-mercator.xml");
@@ -85,7 +86,7 @@ public class WMTSTileSourceTest {
     }
 
     @Test
-    public void testPseudoMercator() throws IOException, WMTSGetCapabilitiesException {
+    void testPseudoMercator() throws IOException, WMTSGetCapabilitiesException {
         ProjectionRegistry.setProjection(Projections.getProjectionByCode("EPSG:3857"));
         WMTSTileSource testSource = new WMTSTileSource(testImageryPSEUDO_MERCATOR);
         testSource.initProjection(ProjectionRegistry.getProjection());
@@ -106,16 +107,16 @@ public class WMTSTileSourceTest {
 
         verifyMercatorTile(testSource, 2 << 9 - 1, 2 << 8 - 1, 10);
 
-        assertEquals("TileXMax", 1, testSource.getTileXMax(0));
-        assertEquals("TileYMax", 1, testSource.getTileYMax(0));
-        assertEquals("TileXMax", 2, testSource.getTileXMax(1));
-        assertEquals("TileYMax", 2, testSource.getTileYMax(1));
-        assertEquals("TileXMax", 4, testSource.getTileXMax(2));
-        assertEquals("TileYMax", 4, testSource.getTileYMax(2));
+        assertEquals(1, testSource.getTileXMax(0), "TileXMax");
+        assertEquals(1, testSource.getTileYMax(0), "TileYMax");
+        assertEquals(2, testSource.getTileXMax(1), "TileXMax");
+        assertEquals(2, testSource.getTileYMax(1), "TileYMax");
+        assertEquals(4, testSource.getTileXMax(2), "TileXMax");
+        assertEquals(4, testSource.getTileYMax(2), "TileYMax");
     }
 
     @Test
-    public void testWALLONIE() throws IOException, WMTSGetCapabilitiesException {
+    void testWALLONIE() throws IOException, WMTSGetCapabilitiesException {
         ProjectionRegistry.setProjection(Projections.getProjectionByCode("EPSG:31370"));
         WMTSTileSource testSource = new WMTSTileSource(testImageryWALLONIE);
         testSource.initProjection(ProjectionRegistry.getProjection());
@@ -134,8 +135,8 @@ public class WMTSTileSourceTest {
     }
 
     @Test
-    @Ignore("disable this test, needs further working") // XXX
-    public void testWALLONIENoMatrixDimension() throws IOException, WMTSGetCapabilitiesException {
+    @Disabled("disable this test, needs further working") // XXX
+    void testWALLONIENoMatrixDimension() throws IOException, WMTSGetCapabilitiesException {
         ProjectionRegistry.setProjection(Projections.getProjectionByCode("EPSG:31370"));
         WMTSTileSource testSource = new WMTSTileSource(getImagery("test/data/wmts/WMTSCapabilities-Wallonie-nomatrixdimension.xml"));
         testSource.initProjection(ProjectionRegistry.getProjection());
@@ -151,15 +152,15 @@ public class WMTSTileSourceTest {
 
     private void verifyBounds(Bounds bounds, WMTSTileSource testSource, int z, int x, int y) {
         LatLon ret = CoordinateConversion.coorToLL(testSource.tileXYToLatLon(x, y, z));
-        assertTrue(ret.toDisplayString() + " doesn't lie within: " + bounds.toString(), bounds.contains(ret));
+        assertTrue(bounds.contains(ret), ret.toDisplayString() + " doesn't lie within: " + bounds);
         int tileXmax = testSource.getTileXMax(z);
         int tileYmax = testSource.getTileYMax(z);
-        assertTrue("tile x: " + x + " is greater than allowed max: " + tileXmax, tileXmax >= x);
-        assertTrue("tile y: " + y + " is greater than allowed max: " + tileYmax, tileYmax >= y);
+        assertTrue(tileXmax >= x, "tile x: " + x + " is greater than allowed max: " + tileXmax);
+        assertTrue(tileYmax >= y, "tile y: " + y + " is greater than allowed max: " + tileYmax);
     }
 
     @Test
-    public void testWIEN() throws IOException, WMTSGetCapabilitiesException {
+    void testWIEN() throws IOException, WMTSGetCapabilitiesException {
         ProjectionRegistry.setProjection(Projections.getProjectionByCode("EPSG:3857"));
         WMTSTileSource testSource = new WMTSTileSource(testImageryWIEN);
         testSource.initProjection(ProjectionRegistry.getProjection());
@@ -194,14 +195,14 @@ public class WMTSTileSourceTest {
         TemplatedTMSTileSource verifier = new TemplatedTMSTileSource(testImageryTMS);
         int result = testSource.getTileXMax(zoom);
         int expected = verifier.getTileXMax(zoom + zoomOffset);
-        assertTrue("TileXMax expected: " + expected + " got: " + result, Math.abs(result - expected) < 5);
+        assertTrue(Math.abs(result - expected) < 5, "TileXMax expected: " + expected + " got: " + result);
         result = testSource.getTileYMax(zoom);
         expected = verifier.getTileYMax(zoom + zoomOffset);
-        assertTrue("TileYMax expected: " + expected + " got: " + result, Math.abs(result - expected) < 5);
+        assertTrue(Math.abs(result - expected) < 5, "TileYMax expected: " + expected + " got: " + result);
     }
 
     @Test
-    public void testGeoportalTOPOPL() throws IOException, WMTSGetCapabilitiesException {
+    void testGeoportalTOPOPL() throws IOException, WMTSGetCapabilitiesException {
         ProjectionRegistry.setProjection(Projections.getProjectionByCode("EPSG:4326"));
         WMTSTileSource testSource = new WMTSTileSource(testImageryTOPO_PL);
         testSource.initProjection(ProjectionRegistry.getProjection());
@@ -209,12 +210,12 @@ public class WMTSTileSourceTest {
         verifyTile(new LatLon(56, 12), testSource, 0, 0, 2);
         verifyTile(new LatLon(51.13231917844218, 16.867680821557823), testSource, 1, 1, 1);
 
-        assertEquals("TileXMax", 2, testSource.getTileXMax(0));
-        assertEquals("TileYMax", 1, testSource.getTileYMax(0));
-        assertEquals("TileXMax", 3, testSource.getTileXMax(1));
-        assertEquals("TileYMax", 2, testSource.getTileYMax(1));
-        assertEquals("TileXMax", 6, testSource.getTileXMax(2));
-        assertEquals("TileYMax", 4, testSource.getTileYMax(2));
+        assertEquals(2, testSource.getTileXMax(0), "TileXMax");
+        assertEquals(1, testSource.getTileYMax(0), "TileYMax");
+        assertEquals(3, testSource.getTileXMax(1), "TileXMax");
+        assertEquals(2, testSource.getTileYMax(1), "TileYMax");
+        assertEquals(6, testSource.getTileXMax(2), "TileXMax");
+        assertEquals(4, testSource.getTileYMax(2), "TileYMax");
         assertEquals(
                 "http://mapy.geoportal.gov.pl/wss/service/WMTS/guest/wmts/TOPO?SERVICE=WMTS&REQUEST=GetTile&"
                 + "VERSION=1.0.0&LAYER=MAPA TOPOGRAFICZNA&STYLE=default&FORMAT=image/jpeg&tileMatrixSet=EPSG:4326&"
@@ -223,7 +224,7 @@ public class WMTSTileSourceTest {
     }
 
     @Test
-    public void testGeoportalORTOPL4326() throws IOException, WMTSGetCapabilitiesException {
+    void testGeoportalORTOPL4326() throws IOException, WMTSGetCapabilitiesException {
         ProjectionRegistry.setProjection(Projections.getProjectionByCode("EPSG:4326"));
         WMTSTileSource testSource = new WMTSTileSource(testImageryORTO_PL);
         testSource.initProjection(ProjectionRegistry.getProjection());
@@ -232,7 +233,7 @@ public class WMTSTileSourceTest {
     }
 
     @Test
-    public void testGeoportalORTOPL2180() throws IOException, WMTSGetCapabilitiesException {
+    void testGeoportalORTOPL2180() throws IOException, WMTSGetCapabilitiesException {
         ProjectionRegistry.setProjection(Projections.getProjectionByCode("EPSG:2180"));
         WMTSTileSource testSource = new WMTSTileSource(testImageryORTO_PL);
         testSource.initProjection(ProjectionRegistry.getProjection());
@@ -242,7 +243,7 @@ public class WMTSTileSourceTest {
     }
 
     @Test
-    public void testTicket12168() throws IOException, WMTSGetCapabilitiesException {
+    void testTicket12168() throws IOException, WMTSGetCapabilitiesException {
         ProjectionRegistry.setProjection(Projections.getProjectionByCode("EPSG:3857"));
         WMTSTileSource testSource = new WMTSTileSource(testImagery12168);
         testSource.initProjection(ProjectionRegistry.getProjection());
@@ -252,7 +253,7 @@ public class WMTSTileSourceTest {
     }
 
     @Test
-    public void testProjectionWithENUAxis() throws IOException, WMTSGetCapabilitiesException {
+    void testProjectionWithENUAxis() throws IOException, WMTSGetCapabilitiesException {
         ProjectionRegistry.setProjection(Projections.getProjectionByCode("EPSG:3346"));
         WMTSTileSource testSource = new WMTSTileSource(testImageryORT2LT);
         testSource.initProjection(ProjectionRegistry.getProjection());
@@ -266,7 +267,7 @@ public class WMTSTileSourceTest {
     }
 
     @Test
-    public void testTwoTileSetsForOneProjection() throws Exception {
+    void testTwoTileSetsForOneProjection() throws Exception {
         ProjectionRegistry.setProjection(Projections.getProjectionByCode("EPSG:3857"));
         ImageryInfo ontario = getImagery(TestUtils.getTestDataRoot() + "wmts/WMTSCapabilities-Ontario.xml");
         ontario.setDefaultLayers(Arrays.asList(new DefaultLayer[] {
@@ -283,7 +284,7 @@ public class WMTSTileSourceTest {
     }
 
     @Test
-    public void testTwoTileSetsForOneProjectionSecondLayer() throws Exception {
+    void testTwoTileSetsForOneProjectionSecondLayer() throws Exception {
         ProjectionRegistry.setProjection(Projections.getProjectionByCode("EPSG:3857"));
         ImageryInfo ontario = getImagery(TestUtils.getTestDataRoot() + "wmts/WMTSCapabilities-Ontario.xml");
         ontario.setDefaultLayers(Arrays.asList(new DefaultLayer[] {
@@ -300,14 +301,14 @@ public class WMTSTileSourceTest {
     }
 
     @Test
-    public void testManyLayersScrollbars() throws Exception {
+    void testManyLayersScrollbars() throws Exception {
         ProjectionRegistry.setProjection(Projections.getProjectionByCode("EPSG:3857"));
         WMTSTileSource testSource = new WMTSTileSource(testLotsOfLayers);
         testSource.initProjection(ProjectionRegistry.getProjection());
     }
 
     @Test
-    public void testParserForDuplicateTags() throws Exception {
+    void testParserForDuplicateTags() throws Exception {
         ProjectionRegistry.setProjection(Projections.getProjectionByCode("EPSG:3857"));
         WMTSTileSource testSource = new WMTSTileSource(testDuplicateTags);
         testSource.initProjection(ProjectionRegistry.getProjection());
@@ -319,14 +320,14 @@ public class WMTSTileSourceTest {
     }
 
     @Test
-    public void testParserForMissingStyleIdentifier() throws Exception {
+    void testParserForMissingStyleIdentifier() throws Exception {
         ProjectionRegistry.setProjection(Projections.getProjectionByCode("EPSG:3857"));
         WMTSTileSource testSource = new WMTSTileSource(testMissingStyleIdentifier);
         testSource.initProjection(ProjectionRegistry.getProjection());
     }
 
     @Test
-    public void testForMultipleTileMatricesForOneLayerProjection() throws Exception {
+    void testForMultipleTileMatricesForOneLayerProjection() throws Exception {
         ProjectionRegistry.setProjection(Projections.getProjectionByCode("EPSG:3857"));
         ImageryInfo copy = new ImageryInfo(testMultipleTileMatrixForLayer);
         List<DefaultLayer> defaultLayers = new ArrayList<>(1);
@@ -347,7 +348,7 @@ public class WMTSTileSourceTest {
      * @throws WMTSGetCapabilitiesException if any error occurs
      */
     @Test
-    public void testDimension() throws IOException, WMTSGetCapabilitiesException {
+    void testDimension() throws IOException, WMTSGetCapabilitiesException {
         ProjectionRegistry.setProjection(Projections.getProjectionByCode("EPSG:21781"));
         ImageryInfo info = new ImageryInfo(testImageryGeoAdminCh);
         List<DefaultLayer> defaultLayers = new ArrayList<>(1);
@@ -362,7 +363,7 @@ public class WMTSTileSourceTest {
     }
 
     @Test
-    public void testDefaultLayer() throws Exception {
+    void testDefaultLayer() throws Exception {
         // https://gibs.earthdata.nasa.gov/wmts/epsg3857/best/1.0.0/WMTSCapabilities.xml
         // do not use withFileBody as it needs different directory layout :(
 
@@ -411,8 +412,8 @@ public class WMTSTileSourceTest {
 
     private void verifyTile(LatLon expected, WMTSTileSource source, int x, int y, int z) {
         LatLon ll = CoordinateConversion.coorToLL(source.tileXYToLatLon(x, y, z));
-        assertEquals("Latitude", expected.lat(), ll.lat(), 1e-05);
-        assertEquals("Longitude", expected.lon(), ll.lon(), 1e-05);
+        assertEquals(expected.lat(), ll.lat(), 1e-05, "Latitude");
+        assertEquals(expected.lon(), ll.lon(), 1e-05, "Longitude");
     }
 
     private void verifyMercatorTile(WMTSTileSource testSource, int x, int y, int z) {
@@ -423,12 +424,12 @@ public class WMTSTileSourceTest {
         TemplatedTMSTileSource verifier = new TemplatedTMSTileSource(testImageryTMS);
         LatLon result = CoordinateConversion.coorToLL(testSource.tileXYToLatLon(x, y, z));
         LatLon expected = CoordinateConversion.coorToLL(verifier.tileXYToLatLon(x, y, z + zoomOffset));
-        assertEquals("Longitude", LatLon.normalizeLon(expected.lon() - result.lon()), 0.0, 1e-04);
-        assertEquals("Latitude", expected.lat(), result.lat(), 1e-04);
+        assertEquals(0.0, LatLon.normalizeLon(expected.lon() - result.lon()), 1e-04, "Longitude");
+        assertEquals(expected.lat(), result.lat(), 1e-04, "Latitude");
     }
 
     @Test
-    public void testGisKtnGvAt() throws IOException, WMTSGetCapabilitiesException {
+    void testGisKtnGvAt() throws IOException, WMTSGetCapabilitiesException {
         ProjectionRegistry.setProjection(Projections.getProjectionByCode("EPSG:31258"));
         final WMTSTileSource source = new WMTSTileSource(testImageryGisKtnGvAt);
         source.initProjection(ProjectionRegistry.getProjection());
diff --git a/test/unit/org/openstreetmap/josm/data/osm/DefaultNameFormatterTest.java b/test/unit/org/openstreetmap/josm/data/osm/DefaultNameFormatterTest.java
index f992b5ac38..3c1ae6e431 100644
--- a/test/unit/org/openstreetmap/josm/data/osm/DefaultNameFormatterTest.java
+++ b/test/unit/org/openstreetmap/josm/data/osm/DefaultNameFormatterTest.java
@@ -4,8 +4,7 @@ package org.openstreetmap.josm.data.osm;
 import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
 import static com.github.tomakehurst.wiremock.client.WireMock.get;
 import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo;
-import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options;
-import static org.junit.Assert.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertEquals;
 
 import java.io.IOException;
 import java.io.InputStream;
@@ -15,37 +14,34 @@ import java.util.List;
 import java.util.stream.Collectors;
 import java.util.stream.IntStream;
 
-import org.junit.Rule;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
 import org.openstreetmap.josm.TestUtils;
 import org.openstreetmap.josm.gui.tagging.presets.TaggingPresetReader;
 import org.openstreetmap.josm.gui.tagging.presets.TaggingPresets;
 import org.openstreetmap.josm.io.IllegalDataException;
 import org.openstreetmap.josm.io.OsmReader;
-import org.openstreetmap.josm.testutils.JOSMTestRules;
+import org.openstreetmap.josm.testutils.annotations.BasicPreferences;
+import org.openstreetmap.josm.testutils.annotations.BasicWiremock;
+import org.openstreetmap.josm.testutils.annotations.HTTP;
 import org.xml.sax.SAXException;
 
-import com.github.tomakehurst.wiremock.junit.WireMockRule;
+import com.github.tomakehurst.wiremock.WireMockServer;
 
 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
 
 /**
  * Unit tests of {@link DefaultNameFormatter} class.
  */
-public class DefaultNameFormatterTest {
-
-    /**
-     * Setup test.
-     */
-    @Rule
-    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
-    public JOSMTestRules test = new JOSMTestRules();
-
+// Preferences are needed for OSM primitives
+@BasicPreferences
+@BasicWiremock
+@HTTP
+class DefaultNameFormatterTest {
     /**
      * HTTP mock.
      */
-    @Rule
-    public WireMockRule wireMockRule = new WireMockRule(options().dynamicPort().usingFilesUnderDirectory(TestUtils.getTestDataRoot()));
+    @BasicWiremock
+    WireMockServer wireMockServer;
 
     /**
      * Non-regression test for ticket <a href="https://josm.openstreetmap.de/ticket/9632">#9632</a>.
@@ -55,14 +51,14 @@ public class DefaultNameFormatterTest {
      */
     @Test
     @SuppressFBWarnings(value = "ITA_INEFFICIENT_TO_ARRAY")
-    public void testTicket9632() throws IllegalDataException, IOException, SAXException {
+    void testTicket9632() throws IllegalDataException, IOException, SAXException {
         String source = "presets/Presets_BicycleJunction-preset.xml";
-        wireMockRule.stubFor(get(urlEqualTo("/" + source))
+        wireMockServer.stubFor(get(urlEqualTo("/" + source))
                 .willReturn(aResponse()
                     .withStatus(200)
                     .withHeader("Content-Type", "text/xml")
                     .withBodyFile(source)));
-        TaggingPresets.addTaggingPresets(TaggingPresetReader.readAll(wireMockRule.url(source), true));
+        TaggingPresets.addTaggingPresets(TaggingPresetReader.readAll(wireMockServer.url(source), true));
 
         Comparator<IRelation<?>> comparator = DefaultNameFormatter.getInstance().getRelationComparator();
 
@@ -101,7 +97,7 @@ public class DefaultNameFormatterTest {
      * Tests formatting of relation names.
      */
     @Test
-    public void testRelationName() {
+    void testRelationName() {
         assertEquals("relation (0, 0 members)",
                 getFormattedRelationName("X=Y"));
         assertEquals("relation (\"Foo\", 0 members)",
@@ -122,7 +118,7 @@ public class DefaultNameFormatterTest {
      * Tests formatting of way names.
      */
     @Test
-    public void testWayName() {
+    void testWayName() {
         assertEquals("\u200Ebuilding\u200E (0 nodes)\u200C", getFormattedWayName("building=yes"));
         assertEquals("\u200EHouse number 123\u200E (0 nodes)\u200C",
                 getFormattedWayName("building=yes addr:housenumber=123"));
@@ -144,7 +140,7 @@ public class DefaultNameFormatterTest {
      * Test of {@link DefaultNameFormatter#formatAsHtmlUnorderedList} methods.
      */
     @Test
-    public void testFormatAsHtmlUnorderedList() {
+    void testFormatAsHtmlUnorderedList() {
         assertEquals("<ul><li>incomplete</li></ul>",
                 DefaultNameFormatter.getInstance().formatAsHtmlUnorderedList(new Node(1)));
 
@@ -158,7 +154,7 @@ public class DefaultNameFormatterTest {
      * Test of {@link DefaultNameFormatter#buildDefaultToolTip(IPrimitive)}.
      */
     @Test
-    public void testBuildDefaultToolTip() {
+    void testBuildDefaultToolTip() {
         assertEquals("<html><strong>id</strong>=0<br>"+
                            "<strong>name:en</strong>=foo<br>"+
                            "<strong>tourism</strong>=hotel<br>"+
@@ -172,7 +168,7 @@ public class DefaultNameFormatterTest {
      * Test of {@link DefaultNameFormatter#removeBiDiCharacters(String)}.
      */
     @Test
-    public void testRemoveBiDiCharacters() {
+    void testRemoveBiDiCharacters() {
         assertEquals("building (0 nodes)", DefaultNameFormatter.removeBiDiCharacters("\u200Ebuilding\u200E (0 nodes)\u200C"));
     }
 }
diff --git a/test/unit/org/openstreetmap/josm/gui/oauth/OsmOAuthAuthorizationClientTest.java b/test/unit/org/openstreetmap/josm/gui/oauth/OsmOAuthAuthorizationClientTest.java
index 983083bd80..bc39d73d2e 100644
--- a/test/unit/org/openstreetmap/josm/gui/oauth/OsmOAuthAuthorizationClientTest.java
+++ b/test/unit/org/openstreetmap/josm/gui/oauth/OsmOAuthAuthorizationClientTest.java
@@ -4,44 +4,40 @@ package org.openstreetmap.josm.gui.oauth;
 import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
 import static com.github.tomakehurst.wiremock.client.WireMock.get;
 import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo;
-import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
 
 import java.net.CookieHandler;
 import java.net.CookieManager;
 import java.net.URI;
 import java.util.Collections;
 
-import org.junit.Rule;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.Timeout;
 import org.openstreetmap.josm.data.oauth.OAuthParameters;
 import org.openstreetmap.josm.data.oauth.OAuthToken;
 import org.openstreetmap.josm.io.OsmTransferCanceledException;
-import org.openstreetmap.josm.testutils.JOSMTestRules;
+import org.openstreetmap.josm.testutils.annotations.BasicPreferences;
+import org.openstreetmap.josm.testutils.annotations.BasicWiremock;
+import org.openstreetmap.josm.testutils.annotations.HTTP;
 
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
-
-import com.github.tomakehurst.wiremock.junit.WireMockRule;
+import com.github.tomakehurst.wiremock.WireMockServer;
 
 /**
  * Unit tests of {@link OsmOAuthAuthorizationClient} class.
  */
-public class OsmOAuthAuthorizationClientTest {
-
-    /**
-     * Setup tests
-     */
-    @Rule
-    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
-    public JOSMTestRules test = new JOSMTestRules().timeout(20000);
-
+@Timeout(20)
+@BasicWiremock
+// Needed for OAuthParameters
+@BasicPreferences
+@HTTP
+class OsmOAuthAuthorizationClientTest {
     /**
      * HTTP mock.
      */
-    @Rule
-    public WireMockRule wireMockRule = new WireMockRule(options().dynamicPort());
+    @BasicWiremock
+    WireMockServer wireMockServer;
 
     /**
      * Unit test of {@link OsmOAuthAuthorizationClient}.
@@ -49,29 +45,30 @@ public class OsmOAuthAuthorizationClientTest {
      * @throws OsmTransferCanceledException  if OSM transfer error occurs
      */
     @Test
-    public void testOsmOAuthAuthorizationClient() throws OsmTransferCanceledException, OsmOAuthAuthorizationException {
+    void testOsmOAuthAuthorizationClient() throws OsmTransferCanceledException, OsmOAuthAuthorizationException {
         // request token
-        wireMockRule.stubFor(get(urlEqualTo("/oauth/request_token"))
+        wireMockServer.stubFor(get(urlEqualTo("/oauth/request_token"))
                 .willReturn(aResponse().withStatus(200).withBody(String.join("&",
                         "oauth_token=entxUGuwRKV6KyVDF0OWScdGhbqXGMGmosXuiChR",
                         "oauth_token_secret=nsBD2Hr5lLGDUeNoh3SnLaGsUV1TiPYM4qUr7tPB"))));
-        OsmOAuthAuthorizationClient client = new OsmOAuthAuthorizationClient(OAuthParameters.createDefault(wireMockRule.url("/api")));
+        OsmOAuthAuthorizationClient client = new OsmOAuthAuthorizationClient(OAuthParameters.createDefault(
+                wireMockServer.url("/api")));
 
         OAuthToken requestToken = client.getRequestToken(null);
-        assertEquals("requestToken.key", "entxUGuwRKV6KyVDF0OWScdGhbqXGMGmosXuiChR", requestToken.getKey());
-        assertEquals("requestToken.secret", "nsBD2Hr5lLGDUeNoh3SnLaGsUV1TiPYM4qUr7tPB", requestToken.getSecret());
+        assertEquals("entxUGuwRKV6KyVDF0OWScdGhbqXGMGmosXuiChR", requestToken.getKey(), "requestToken.key");
+        assertEquals("nsBD2Hr5lLGDUeNoh3SnLaGsUV1TiPYM4qUr7tPB", requestToken.getSecret(), "requestToken.secret");
         String url = client.getAuthoriseUrl(requestToken);
-        assertEquals("url", wireMockRule.url("/oauth/authorize?oauth_token=entxUGuwRKV6KyVDF0OWScdGhbqXGMGmosXuiChR"), url);
+        assertEquals(wireMockServer.url("/oauth/authorize?oauth_token=entxUGuwRKV6KyVDF0OWScdGhbqXGMGmosXuiChR"), url, "url");
 
         // access token
-        wireMockRule.stubFor(get(urlEqualTo("/oauth/access_token"))
+        wireMockServer.stubFor(get(urlEqualTo("/oauth/access_token"))
                 .willReturn(aResponse().withStatus(200).withBody(String.join("&",
                         "oauth_token=eGMGmosXuiChRntxUGuwRKV6KyVDF0OWScdGhbqX",
                         "oauth_token_secret=nsBUeNor7tPh3SHr5lLaGsGDUD2PYMV1TinL4qUB"))));
 
         OAuthToken accessToken = client.getAccessToken(null);
-        assertEquals("accessToken.key", "eGMGmosXuiChRntxUGuwRKV6KyVDF0OWScdGhbqX", accessToken.getKey());
-        assertEquals("accessToken.secret", "nsBUeNor7tPh3SHr5lLaGsGDUD2PYMV1TinL4qUB", accessToken.getSecret());
+        assertEquals("eGMGmosXuiChRntxUGuwRKV6KyVDF0OWScdGhbqX", accessToken.getKey(), "accessToken.key");
+        assertEquals("nsBUeNor7tPh3SHr5lLaGsGDUD2PYMV1TinL4qUB", accessToken.getSecret(), "accessToken.secret");
     }
 
     /**
@@ -81,21 +78,21 @@ public class OsmOAuthAuthorizationClientTest {
      * @throws Exception if any error occurs
      */
     @Test
-    public void testCookieHandlingMock() throws Exception {
-        wireMockRule.stubFor(get(urlEqualTo("/login?cookie_test=true"))
+    void testCookieHandlingMock() throws Exception {
+        wireMockServer.stubFor(get(urlEqualTo("/login?cookie_test=true"))
                 .willReturn(aResponse()
                         .withStatus(200)
                         .withHeader("Set-Cookie", "_osm_session=7fe8e2ea36c6b803cb902301b28e0a; path=/; HttpOnly; SameSite=Lax")
                 .withBody("<input type=\"hidden\" " +
                         "name=\"authenticity_token\" " +
                         "value=\"fzp6CWJhp6Vns09re3s2Tw==\" />")));
-        final OAuthParameters parameters = OAuthParameters.createDefault(wireMockRule.url("/api"));
+        final OAuthParameters parameters = OAuthParameters.createDefault(wireMockServer.url("/api"));
         final OsmOAuthAuthorizationClient client = new OsmOAuthAuthorizationClient(parameters);
         final OsmOAuthAuthorizationClient.SessionId sessionId = client.fetchOsmWebsiteSessionId();
         assertNotNull(sessionId);
-        assertEquals("sessionId.id", "7fe8e2ea36c6b803cb902301b28e0a", sessionId.id);
-        assertEquals("sessionId.token", "fzp6CWJhp6Vns09re3s2Tw==", sessionId.token);
-        assertNull("sessionId.userName", sessionId.userName);
+        assertEquals("7fe8e2ea36c6b803cb902301b28e0a", sessionId.id, "sessionId.id");
+        assertEquals("fzp6CWJhp6Vns09re3s2Tw==", sessionId.token, "sessionId.token");
+        assertNull(sessionId.userName, "sessionId.userName");
     }
 
     /**
@@ -105,7 +102,7 @@ public class OsmOAuthAuthorizationClientTest {
      * @throws Exception if any error occurs
      */
     @Test
-    public void testCookieHandlingCookieManager() throws Exception {
+    void testCookieHandlingCookieManager() throws Exception {
         // emulate Java Web Start behaviour
         // see https://docs.oracle.com/javase/tutorial/deployment/doingMoreWithRIA/accessingCookies.html
         final OAuthParameters parameters = OAuthParameters.createDefault();
diff --git a/test/unit/org/openstreetmap/josm/gui/preferences/server/ApiUrlTestTaskTest.java b/test/unit/org/openstreetmap/josm/gui/preferences/server/ApiUrlTestTaskTest.java
index ed9caf0bed..87210a150c 100644
--- a/test/unit/org/openstreetmap/josm/gui/preferences/server/ApiUrlTestTaskTest.java
+++ b/test/unit/org/openstreetmap/josm/gui/preferences/server/ApiUrlTestTaskTest.java
@@ -4,60 +4,54 @@ package org.openstreetmap.josm.gui.preferences.server;
 import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
 import static com.github.tomakehurst.wiremock.client.WireMock.get;
 import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo;
-import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options;
 import static org.hamcrest.CoreMatchers.containsString;
 import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-
-import java.awt.Component;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
 
 import javax.swing.JLabel;
+import java.awt.Component;
 
-import org.junit.Rule;
-import org.junit.Test;
-import org.openstreetmap.josm.TestUtils;
-import org.openstreetmap.josm.testutils.JOSMTestRules;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.Timeout;
+import org.openstreetmap.josm.testutils.annotations.BasicPreferences;
+import org.openstreetmap.josm.testutils.annotations.BasicWiremock;
+import org.openstreetmap.josm.testutils.annotations.HTTP;
 import org.openstreetmap.josm.tools.Logging;
 
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
-
-import com.github.tomakehurst.wiremock.junit.WireMockRule;
+import com.github.tomakehurst.wiremock.WireMockServer;
 
 /**
  * Unit tests of {@link ApiUrlTestTask} class.
  */
-public class ApiUrlTestTaskTest {
-
-    /**
-     * Setup tests
-     */
-    @Rule
-    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
-    public JOSMTestRules test = new JOSMTestRules().preferences().timeout(30000);
-
+@Timeout(30)
+@BasicPreferences
+@BasicWiremock
+@HTTP
+class ApiUrlTestTaskTest {
     /**
      * HTTP mock.
      */
-    @Rule
-    public WireMockRule wireMockRule = new WireMockRule(options().dynamicPort().usingFilesUnderDirectory(TestUtils.getTestDataRoot()));
+    @BasicWiremock
+    WireMockServer wireMockServer;
 
     private static final Component PARENT = new JLabel();
 
     /**
      * Unit test of {@link ApiUrlTestTask#ApiUrlTestTask} - null url.
      */
-    @Test(expected = IllegalArgumentException.class)
-    public void testNullApiUrl() {
-        new ApiUrlTestTask(PARENT, null);
+    @Test
+    void testNullApiUrl() {
+        assertThrows(IllegalArgumentException.class, () -> new ApiUrlTestTask(PARENT, null));
     }
 
     /**
      * Unit test of {@link ApiUrlTestTask} - nominal url.
      */
     @Test
-    public void testNominalUrl() {
-        ApiUrlTestTask task = new ApiUrlTestTask(PARENT, wireMockRule.url("/__files/api"));
+    void testNominalUrl() {
+        ApiUrlTestTask task = new ApiUrlTestTask(PARENT, wireMockServer.url("/__files/api"));
         task.run();
         assertTrue(task.isSuccess());
     }
@@ -66,7 +60,7 @@ public class ApiUrlTestTaskTest {
      * Unit test of {@link ApiUrlTestTask#alertInvalidUrl} - malformed url.
      */
     @Test
-    public void testAlertInvalidUrl() {
+    void testAlertInvalidUrl() {
         Logging.clearLastErrorAndWarnings();
         ApiUrlTestTask task = new ApiUrlTestTask(PARENT, "malformed url");
         task.run();
@@ -79,7 +73,7 @@ public class ApiUrlTestTaskTest {
      * Unit test of {@link ApiUrlTestTask} - unknown host.
      */
     @Test
-    public void testUnknownHost() {
+    void testUnknownHost() {
         Logging.clearLastErrorAndWarnings();
         ApiUrlTestTask task = new ApiUrlTestTask(PARENT, "http://unknown");
         task.run();
@@ -92,12 +86,12 @@ public class ApiUrlTestTaskTest {
      * Unit test of {@link ApiUrlTestTask#alertInvalidServerResult} - http 404.
      */
     @Test
-    public void testAlertInvalidServerResult() {
+    void testAlertInvalidServerResult() {
         Logging.clearLastErrorAndWarnings();
-        wireMockRule.stubFor(get(urlEqualTo("/does-not-exist/0.6/capabilities"))
+        wireMockServer.stubFor(get(urlEqualTo("/does-not-exist/0.6/capabilities"))
                 .willReturn(aResponse().withStatus(404)));
 
-        ApiUrlTestTask task = new ApiUrlTestTask(PARENT, wireMockRule.url("/does-not-exist"));
+        ApiUrlTestTask task = new ApiUrlTestTask(PARENT, wireMockServer.url("/does-not-exist"));
         task.run();
         assertFalse(task.isSuccess());
         assertThat(Logging.getLastErrorAndWarnings().toString(), containsString(
@@ -108,13 +102,13 @@ public class ApiUrlTestTaskTest {
      * Unit test of {@link ApiUrlTestTask#alertInvalidCapabilities} - invalid contents.
      */
     @Test
-    public void testAlertInvalidCapabilities() {
+    void testAlertInvalidCapabilities() {
         Logging.clearLastErrorAndWarnings();
-        ApiUrlTestTask task = new ApiUrlTestTask(PARENT, wireMockRule.url("/__files/invalid_api"));
+        ApiUrlTestTask task = new ApiUrlTestTask(PARENT, wireMockServer.url("/__files/invalid_api"));
         task.run();
         assertFalse(task.isSuccess());
         assertThat(Logging.getLastErrorAndWarnings().toString(), containsString(
                 "The OSM API server at 'XXX' did not return a valid response.<br>It is likely that 'XXX' is not an OSM API server."
-                        .replace("XXX", wireMockRule.url("/__files/invalid_api"))));
+                        .replace("XXX", wireMockServer.url("/__files/invalid_api"))));
     }
 }
diff --git a/test/unit/org/openstreetmap/josm/io/OsmServerHistoryReaderTest.java b/test/unit/org/openstreetmap/josm/io/OsmServerHistoryReaderTest.java
index 93611636ae..fd689452de 100644
--- a/test/unit/org/openstreetmap/josm/io/OsmServerHistoryReaderTest.java
+++ b/test/unit/org/openstreetmap/josm/io/OsmServerHistoryReaderTest.java
@@ -1,42 +1,42 @@
 // License: GPL. For details, see LICENSE file.
 package org.openstreetmap.josm.io;
 
-import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options;
-import static org.junit.Assert.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertEquals;
 
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.openstreetmap.josm.JOSMFixture;
-import org.openstreetmap.josm.TestUtils;
+import java.time.Instant;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
 import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
 import org.openstreetmap.josm.data.osm.history.History;
 import org.openstreetmap.josm.data.osm.history.HistoryDataSet;
 import org.openstreetmap.josm.gui.progress.NullProgressMonitor;
 import org.openstreetmap.josm.spi.preferences.Config;
+import org.openstreetmap.josm.testutils.annotations.BasicPreferences;
+import org.openstreetmap.josm.testutils.annotations.BasicWiremock;
+import org.openstreetmap.josm.testutils.annotations.HTTP;
 
-import com.github.tomakehurst.wiremock.junit.WireMockRule;
-
-import java.time.Instant;
+import com.github.tomakehurst.wiremock.WireMockServer;
 
 /**
  * Unit tests of {@link OsmServerHistoryReader} class.
  */
-public class OsmServerHistoryReaderTest {
-
+@BasicPreferences
+@BasicWiremock
+@HTTP
+class OsmServerHistoryReaderTest {
     /**
      * HTTP mock.
      */
-    @Rule
-    public WireMockRule wireMockRule = new WireMockRule(options().dynamicPort().usingFilesUnderDirectory(TestUtils.getTestDataRoot()));
+    @BasicWiremock
+    WireMockServer wireMockServer;
 
     /**
      * Setup tests.
      */
-    @Before
-    public void setUp() {
-        JOSMFixture.createUnitTestFixture().init();
-        Config.getPref().put("osm-server.url", wireMockRule.url("/__files/api"));
+    @BeforeEach
+    void setUp() {
+        Config.getPref().put("osm-server.url", wireMockServer.url("/__files/api"));
     }
 
     /**
@@ -44,7 +44,7 @@ public class OsmServerHistoryReaderTest {
      * @throws OsmTransferException if any error occurs
      */
     @Test
-    public void testNode() throws OsmTransferException {
+    void testNode() throws OsmTransferException {
         OsmServerHistoryReader reader = new OsmServerHistoryReader(OsmPrimitiveType.NODE, 266187);
         HistoryDataSet ds = reader.parseHistory(NullProgressMonitor.INSTANCE);
         History h = ds.getHistory(266187, OsmPrimitiveType.NODE);
@@ -59,7 +59,7 @@ public class OsmServerHistoryReaderTest {
      * @throws OsmTransferException if any error occurs
      */
     @Test
-    public void testWay() throws OsmTransferException {
+    void testWay() throws OsmTransferException {
         OsmServerHistoryReader reader = new OsmServerHistoryReader(OsmPrimitiveType.WAY, 3058844);
         HistoryDataSet ds = reader.parseHistory(NullProgressMonitor.INSTANCE);
         History h = ds.getHistory(3058844, OsmPrimitiveType.WAY);
@@ -79,7 +79,7 @@ public class OsmServerHistoryReaderTest {
      * @throws OsmTransferException if any error occurs
      */
     @Test
-    public void testRelation() throws OsmTransferException {
+    void testRelation() throws OsmTransferException {
         OsmServerHistoryReader reader = new OsmServerHistoryReader(OsmPrimitiveType.RELATION, 49);
         HistoryDataSet ds = reader.parseHistory(NullProgressMonitor.INSTANCE);
         History h = ds.getHistory(49, OsmPrimitiveType.RELATION);
diff --git a/test/unit/org/openstreetmap/josm/io/OverpassDownloadReaderTest.java b/test/unit/org/openstreetmap/josm/io/OverpassDownloadReaderTest.java
index 42650782ab..3c163f53b0 100644
--- a/test/unit/org/openstreetmap/josm/io/OverpassDownloadReaderTest.java
+++ b/test/unit/org/openstreetmap/josm/io/OverpassDownloadReaderTest.java
@@ -4,57 +4,49 @@ package org.openstreetmap.josm.io;
 import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
 import static com.github.tomakehurst.wiremock.client.WireMock.get;
 import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo;
-import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
 
 import java.io.StringReader;
 import java.time.LocalDateTime;
 import java.util.regex.Matcher;
 
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.openstreetmap.josm.TestUtils;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
 import org.openstreetmap.josm.data.Bounds;
 import org.openstreetmap.josm.io.OverpassDownloadReader.OverpassOutputFormat;
-import org.openstreetmap.josm.testutils.JOSMTestRules;
+import org.openstreetmap.josm.testutils.annotations.BasicPreferences;
+import org.openstreetmap.josm.testutils.annotations.BasicWiremock;
+import org.openstreetmap.josm.testutils.annotations.HTTP;
 import org.openstreetmap.josm.tools.SearchCompilerQueryWizard;
 import org.openstreetmap.josm.tools.Utils;
 import org.openstreetmap.josm.tools.date.DateUtils;
 
-import com.github.tomakehurst.wiremock.junit.WireMockRule;
-
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+import com.github.tomakehurst.wiremock.WireMockServer;
 
 /**
  * Unit tests of {@link OverpassDownloadReader} class.
  */
-public class OverpassDownloadReaderTest {
-
-    /**
-     * Base test environment is enough
-     */
-    @Rule
-    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
-    public JOSMTestRules test = new JOSMTestRules().preferences();
-
+@BasicWiremock
+@BasicPreferences
+@HTTP
+class OverpassDownloadReaderTest {
     /**
      * HTTP mock.
      */
-    @Rule
-    public WireMockRule wireMockRule = new WireMockRule(options().dynamicPort().usingFilesUnderDirectory(TestUtils.getTestDataRoot()));
+    @BasicWiremock
+    WireMockServer wireMockServer;
 
     private static final String NOMINATIM_URL_PATH = "/search?format=xml&q=";
 
     /**
      * Setup test.
      */
-    @Before
+    @BeforeEach
     public void setUp() {
-        NameFinder.NOMINATIM_URL_PROP.put(wireMockRule.url(NOMINATIM_URL_PATH));
+        NameFinder.NOMINATIM_URL_PROP.put(wireMockServer.url(NOMINATIM_URL_PATH));
     }
 
     private String getExpandedQuery(String search) {
@@ -69,7 +61,7 @@ public class OverpassDownloadReaderTest {
      * Tests evaluating the extended query feature {@code bbox}.
      */
     @Test
-    public void testBbox() {
+    void testBbox() {
         final String query = getExpandedQuery("amenity=drinking_water");
         assertEquals("" +
                 "[out:xml][timeout:90][bbox:2.0,1.0,4.0,3.0];\n" +
@@ -81,7 +73,7 @@ public class OverpassDownloadReaderTest {
     }
 
     private void stubNominatim(String query) {
-        wireMockRule.stubFor(get(urlEqualTo(NOMINATIM_URL_PATH + query))
+        wireMockServer.stubFor(get(urlEqualTo(NOMINATIM_URL_PATH + query))
                 .willReturn(aResponse()
                     .withStatus(200)
                     .withHeader("Content-Type", "text/xml")
@@ -92,7 +84,7 @@ public class OverpassDownloadReaderTest {
      * Tests evaluating the extended query feature {@code date}.
      */
     @Test
-    public void testDate() {
+    void testDate() {
         LocalDateTime from = LocalDateTime.of(2017, 7, 14, 2, 40);
         assertEquals("2016-07-14T02:40:00Z", OverpassDownloadReader.date("1 year", from));
         assertEquals("2007-07-14T02:40:00Z", OverpassDownloadReader.date("10years", from));
@@ -118,7 +110,7 @@ public class OverpassDownloadReaderTest {
      * Tests evaluating the extended query feature {@code date} through {@code newer:} operator.
      */
     @Test
-    public void testDateNewer() {
+    void testDateNewer() {
         String query = getExpandedQuery("type:node and newer:3minutes");
         String statement = query.substring(query.indexOf("node(newer:\"") + 12, query.lastIndexOf("\");"));
         assertNotNull(DateUtils.fromString(statement));
@@ -132,7 +124,7 @@ public class OverpassDownloadReaderTest {
      * Tests evaluating the extended query feature {@code geocodeArea}.
      */
     @Test
-    public void testGeocodeArea() {
+    void testGeocodeArea() {
         stubNominatim("London");
         final String query = getExpandedQuery("amenity=drinking_water in London");
         assertEquals("" +
@@ -149,7 +141,7 @@ public class OverpassDownloadReaderTest {
      * Tests evaluating the extended query feature {@code geocodeArea}.
      */
     @Test
-    public void testGeocodeUnknownArea() {
+    void testGeocodeUnknownArea() {
         stubNominatim("foo-bar-baz-does-not-exist");
         final String query = OverpassDownloadReader.expandExtendedQueries("{{geocodeArea:foo-bar-baz-does-not-exist}}");
         assertEquals("// Failed to evaluate {{geocodeArea:foo-bar-baz-does-not-exist}}\n", query);
@@ -159,7 +151,7 @@ public class OverpassDownloadReaderTest {
      * Tests evaluating the overpass output format statements.
      */
     @Test
-    public void testOutputFormatStatement() {
+    void testOutputFormatStatement() {
         for (OverpassOutputFormat oof : OverpassOutputFormat.values()) {
             Matcher m = OverpassDownloadReader.OUTPUT_FORMAT_STATEMENT.matcher("[out:"+oof.getDirective()+"]");
             assertTrue(m.matches());
@@ -181,7 +173,7 @@ public class OverpassDownloadReaderTest {
      * Test {@link OverpassDownloadReader#fixQuery(String)}.
      */
     @Test
-    public void testFixQuery() {
+    void testFixQuery() {
         assertNull(OverpassDownloadReader.fixQuery(null));
 
         assertEquals("out meta;", OverpassDownloadReader.fixQuery("out;"));
@@ -223,7 +215,7 @@ public class OverpassDownloadReaderTest {
      * @throws Exception if an error occurs
      */
     @Test
-    public void testSearchName() throws Exception {
+    void testSearchName() throws Exception {
         try (StringReader reader = new StringReader(NameFinderTest.SAMPLE)) {
             assertEquals(1942586L,
                     OverpassDownloadReader.searchName(NameFinder.parseSearchResults(reader)).getOsmId().getUniqueId());
diff --git a/test/unit/org/openstreetmap/josm/io/imagery/WMSImageryTest.java b/test/unit/org/openstreetmap/josm/io/imagery/WMSImageryTest.java
index 0b449ad141..e922a848b8 100644
--- a/test/unit/org/openstreetmap/josm/io/imagery/WMSImageryTest.java
+++ b/test/unit/org/openstreetmap/josm/io/imagery/WMSImageryTest.java
@@ -1,47 +1,48 @@
 // License: GPL. For details, see LICENSE file.
 package org.openstreetmap.josm.io.imagery;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
 
 import java.io.IOException;
 import java.nio.file.Files;
 import java.nio.file.Paths;
 import java.util.List;
 
-import org.junit.Rule;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.RegisterExtension;
 import org.openstreetmap.josm.TestUtils;
 import org.openstreetmap.josm.io.imagery.WMSImagery.WMSGetCapabilitiesException;
 import org.openstreetmap.josm.testutils.JOSMTestRules;
+import org.openstreetmap.josm.testutils.annotations.BasicWiremock;
 
+import com.github.tomakehurst.wiremock.WireMockServer;
 import com.github.tomakehurst.wiremock.client.WireMock;
-import com.github.tomakehurst.wiremock.core.WireMockConfiguration;
-import com.github.tomakehurst.wiremock.junit.WireMockRule;
 
 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
 
 /**
  * Unit tests of {@link WMSImagery} class.
  */
-public class WMSImageryTest {
+@BasicWiremock
+class WMSImageryTest {
 
     /**
      * Setup test
      */
-    @Rule
+    @RegisterExtension
     @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
-    public JOSMTestRules test = new JOSMTestRules().projection();
+    JOSMTestRules test = new JOSMTestRules().projection();
+
+    @BasicWiremock
+    WireMockServer tileServer;
 
-    @Rule
-    public WireMockRule tileServer = new WireMockRule(WireMockConfiguration.options()
-            .dynamicPort());
     /**
      * Unit test of {@code WMSImagery.WMSGetCapabilitiesException} class
      */
     @Test
-    public void testWMSGetCapabilitiesException() {
+    void testWMSGetCapabilitiesException() {
         Exception cause = new Exception("test");
         WMSGetCapabilitiesException exc = new WMSGetCapabilitiesException(cause, "bar");
         assertEquals(cause, exc.getCause());
@@ -57,7 +58,7 @@ public class WMSImageryTest {
      * @throws WMSGetCapabilitiesException never
      */
     @Test
-    public void testTicket15730() throws IOException, WMSGetCapabilitiesException {
+    void testTicket15730() throws IOException, WMSGetCapabilitiesException {
         tileServer.stubFor(WireMock.get(WireMock.anyUrl()).willReturn(WireMock.aResponse().withBody(
                 Files.readAllBytes(Paths.get(TestUtils.getRegressionDataDir(15730), "capabilities.xml"))
                 )));
@@ -68,7 +69,7 @@ public class WMSImageryTest {
     }
 
     @Test
-    public void testNestedLayers() throws Exception {
+    void testNestedLayers() throws Exception {
         tileServer.stubFor(WireMock.get(WireMock.anyUrl()).willReturn(WireMock.aResponse().withBody(
                 Files.readAllBytes(Paths.get(TestUtils.getTestDataRoot() + "wms/mapa-um-warszawa-pl.xml")))));
         WMSImagery wmsi = new WMSImagery(tileServer.url("/serwis"));
@@ -83,7 +84,7 @@ public class WMSImageryTest {
      * @throws WMSGetCapabilitiesException never
      */
     @Test
-    public void testTicket16248() throws IOException, WMSGetCapabilitiesException {
+    void testTicket16248() throws IOException, WMSGetCapabilitiesException {
         byte[] capabilities = Files.readAllBytes(Paths.get(TestUtils.getRegressionDataFile(16248, "capabilities.xml")));
         tileServer.stubFor(WireMock.get(WireMock.anyUrl()).willReturn(WireMock.aResponse().withBody(capabilities)));
         WMSImagery wms = new WMSImagery(tileServer.url("any"));
@@ -100,7 +101,7 @@ public class WMSImageryTest {
      * @throws WMSGetCapabilitiesException never
      */
     @Test
-    public void testTicket19193() throws IOException, WMSGetCapabilitiesException {
+    void testTicket19193() throws IOException, WMSGetCapabilitiesException {
         byte[] capabilities = Files.readAllBytes(Paths.get(TestUtils.getRegressionDataFile(19193, "capabilities.xml")));
         tileServer.stubFor(WireMock.get(WireMock.anyUrl()).willReturn(WireMock.aResponse().withBody(capabilities)));
         WMSImagery wms = new WMSImagery(tileServer.url("any"));
@@ -119,7 +120,7 @@ public class WMSImageryTest {
      * @throws WMSGetCapabilitiesException  never
      */
     @Test
-    public void testTicket16333() throws IOException, WMSGetCapabilitiesException {
+    void testTicket16333() throws IOException, WMSGetCapabilitiesException {
         tileServer.stubFor(
                 WireMock.get(WireMock.anyUrl())
                 .willReturn(WireMock.aResponse().withBody(
@@ -128,7 +129,7 @@ public class WMSImageryTest {
         );
         WMSImagery wms = new WMSImagery(tileServer.url("any"));
         assertEquals("https://duinoord.xs4all.nl/geoserver/ows?SERVICE=WMS&", wms.buildRootUrl());
-        assertEquals(null, wms.getLayers().get(0).getName());
+        assertNull(wms.getLayers().get(0).getName());
         assertEquals("", wms.getLayers().get(0).getTitle());
 
         assertEquals("bag:Matching Street", wms.getLayers().get(0).getChildren().get(0).getName());
@@ -136,7 +137,7 @@ public class WMSImageryTest {
     }
 
     @Test
-    public void testForTitleWithinAttribution_ticket16940() throws IOException, WMSGetCapabilitiesException {
+    void testForTitleWithinAttribution_ticket16940() throws IOException, WMSGetCapabilitiesException {
         tileServer.stubFor(
                 WireMock.get(WireMock.anyUrl())
                 .willReturn(WireMock.aResponse().withBody(
@@ -147,4 +148,3 @@ public class WMSImageryTest {
         assertEquals("Hipsográfico", wms.getLayers().stream().findFirst().get().getTitle());
     }
 }
-
diff --git a/test/unit/org/openstreetmap/josm/testutils/annotations/AnnotationUtils.java b/test/unit/org/openstreetmap/josm/testutils/annotations/AnnotationUtils.java
index 858150b44d..c297efd461 100644
--- a/test/unit/org/openstreetmap/josm/testutils/annotations/AnnotationUtils.java
+++ b/test/unit/org/openstreetmap/josm/testutils/annotations/AnnotationUtils.java
@@ -16,7 +16,7 @@ import org.junit.platform.commons.support.AnnotationSupport;
  * @author Taylor Smock
  * @since 18037
  */
-final class AnnotationUtils {
+public final class AnnotationUtils {
     private AnnotationUtils() {
         // Utils class
     }
diff --git a/test/unit/org/openstreetmap/josm/testutils/annotations/BasicWiremock.java b/test/unit/org/openstreetmap/josm/testutils/annotations/BasicWiremock.java
new file mode 100644
index 0000000000..6161b509ed
--- /dev/null
+++ b/test/unit/org/openstreetmap/josm/testutils/annotations/BasicWiremock.java
@@ -0,0 +1,218 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.testutils.annotations;
+
+import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.fail;
+
+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 java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import org.junit.jupiter.api.extension.AfterAllCallback;
+import org.junit.jupiter.api.extension.AfterEachCallback;
+import org.junit.jupiter.api.extension.BeforeAllCallback;
+import org.junit.jupiter.api.extension.BeforeEachCallback;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.junit.jupiter.api.extension.ExtensionContext;
+import org.junit.jupiter.api.extension.ParameterContext;
+import org.junit.jupiter.api.extension.ParameterResolutionException;
+import org.junit.jupiter.api.extension.ParameterResolver;
+import org.junit.platform.commons.support.AnnotationSupport;
+import org.openstreetmap.josm.TestUtils;
+import org.openstreetmap.josm.gui.progress.NullProgressMonitor;
+import org.openstreetmap.josm.gui.util.GuiHelper;
+import org.openstreetmap.josm.io.OsmApi;
+import org.openstreetmap.josm.spi.preferences.Config;
+import org.openstreetmap.josm.tools.Logging;
+import org.openstreetmap.josm.tools.Pair;
+import org.openstreetmap.josm.tools.Utils;
+
+import com.github.tomakehurst.wiremock.WireMockServer;
+import com.github.tomakehurst.wiremock.extension.ResponseTransformer;
+import com.github.tomakehurst.wiremock.verification.LoggedRequest;
+
+/**
+ * Create a basic wiremock environment. If you need the actual WireMockServer, annotate a field or parameter
+ * with {@code @BasicWiremock}.
+ *
+ * @author Taylor Smock
+ * @see OsmApiExtension (this sets the Osm Api to the wiremock URL)
+ * @since xxx
+ */
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ ElementType.TYPE, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD})
+@ExtendWith(BasicWiremock.WireMockExtension.class)
+public @interface BasicWiremock {
+    /**
+     * Set the path for the data. Default is {@link TestUtils#getTestDataRoot()}.
+     * @return The path ({@code ""} for the default)
+     */
+    String value() default "";
+
+    /**
+     * {@link ResponseTransformer} for use with the WireMock server.
+     * Current constructors supported:
+     * <ul>
+     *     <li>{@code new ResponseTransformer()}</li>
+     *     <li>{@code new ResponseTransformer(ExtensionContext context)}</li>
+     * </ul>
+     * @return The transformers to instantiate
+     */
+    Class<? extends ResponseTransformer>[] responseTransformers() default {};
+
+    /**
+     * Start/stop WireMock automatically, and check for missed calls.
+     * @author Taylor Smock
+     *
+     */
+    class WireMockExtension
+            implements AfterAllCallback, AfterEachCallback, BeforeAllCallback, BeforeEachCallback, ParameterResolver {
+        /**
+         * Get the default wiremock server
+         * @param context The context to search
+         * @return The wiremock server
+         */
+        static WireMockServer getWiremock(ExtensionContext context) {
+            ExtensionContext.Namespace namespace = ExtensionContext.Namespace.create(BasicWiremock.class);
+            BasicWiremock annotation = AnnotationUtils.findFirstParentAnnotation(context, BasicWiremock.class)
+                    .orElseThrow(() -> new IllegalArgumentException("There must be a @BasicWiremock annotation"));
+            return context.getStore(namespace).getOrComputeIfAbsent(WireMockServer.class, clazz -> {
+                final List<ResponseTransformer> transformers = new ArrayList<>(annotation.responseTransformers().length);
+                for (Class<? extends ResponseTransformer> responseTransformer : annotation.responseTransformers()) {
+                    for (Pair<Class<?>[], Object[]> parameterMapping : Arrays.asList(
+                            new Pair<>(new Class<?>[] {ExtensionContext.class }, new Object[] {context }),
+                            new Pair<>(new Class<?>[0], new Object[0]))) {
+                        try {
+                            Constructor<? extends ResponseTransformer> constructor = responseTransformer
+                                    .getConstructor(parameterMapping.a);
+                            transformers.add(constructor.newInstance(parameterMapping.b));
+                            break;
+                        } catch (ReflectiveOperationException e) {
+                            fail(e);
+                        }
+                    }
+                }
+                return new WireMockServer(
+                    options().usingFilesUnderDirectory(Utils.isStripEmpty(annotation.value()) ? TestUtils.getTestDataRoot() :
+                            annotation.value()).extensions(transformers.toArray(new ResponseTransformer[0])).dynamicPort());
+            }, WireMockServer.class);
+        }
+
+        /**
+         * Replace URL servers with wiremock
+         *
+         * @param wireMockServer The wiremock to point to
+         * @param url            The URL to fix
+         * @return A url that points at the wiremock server
+         */
+        public static String replaceUrl(WireMockServer wireMockServer, String url) {
+            try {
+                URL temp = new URL(url);
+                return wireMockServer.baseUrl() + temp.getFile();
+            } catch (MalformedURLException error) {
+                Logging.error(error);
+            }
+            return null;
+        }
+
+        @Override
+        public void afterAll(ExtensionContext context) throws Exception {
+            // Run in EDT to avoid stopping wiremock server before wiremock requests finish.
+            GuiHelper.runInEDTAndWait(getWiremock(context)::stop);
+        }
+
+        @Override
+        public void afterEach(ExtensionContext context) throws Exception {
+            List<LoggedRequest> missed = getWiremock(context).findUnmatchedRequests().getRequests();
+            missed.forEach(r -> Logging.error(r.getAbsoluteUrl()));
+            try {
+                assertTrue(missed.isEmpty(), missed.stream().map(LoggedRequest::getUrl).collect(Collectors.joining("\n\n")));
+            } finally {
+                getWiremock(context).resetRequests();
+                getWiremock(context).resetToDefaultMappings();
+                getWiremock(context).resetScenarios();
+                if (AnnotationUtils.elementIsAnnotated(context.getElement(), BasicWiremock.class)
+                        || getWiremock(context) == null) {
+                    this.afterAll(context);
+                }
+            }
+        }
+
+        @Override
+        public void beforeAll(ExtensionContext context) throws Exception {
+            getWiremock(context).start();
+        }
+
+        @Override
+        public void beforeEach(ExtensionContext context) throws Exception {
+            if (AnnotationUtils.elementIsAnnotated(context.getElement(), BasicWiremock.class) || getWiremock(context) == null) {
+                this.beforeAll(context);
+            }
+            if (context.getTestClass().isPresent()) {
+                List<Field> wireMockFields = AnnotationSupport.findAnnotatedFields(context.getRequiredTestClass(), BasicWiremock.class);
+                for (Field field : wireMockFields) {
+                    if (WireMockServer.class.isAssignableFrom(field.getType())) {
+                        final boolean isAccessible = field.isAccessible();
+                        field.setAccessible(true);
+                        try {
+                            field.set(context.getTestInstance().orElse(null), getWiremock(context));
+                        } finally {
+                            field.setAccessible(isAccessible);
+                        }
+                    } else {
+                        throw new IllegalArgumentException("@BasicWiremock: cannot set field of type " + field.getType().getName());
+                    }
+                }
+            }
+        }
+
+        @Override
+        public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext)
+                throws ParameterResolutionException {
+            return parameterContext.getParameter().getAnnotation(BasicWiremock.class) != null
+                    && parameterContext.getParameter().getType() == WireMockServer.class;
+        }
+
+        @Override
+        public Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext)
+                throws ParameterResolutionException {
+            return getWiremock(extensionContext);
+        }
+    }
+
+    /**
+     * A class specifically to mock OSM API calls
+     */
+    class OsmApiExtension extends WireMockExtension {
+        @Override
+        public void afterAll(ExtensionContext context) throws Exception {
+            try {
+                super.afterAll(context);
+            } finally {
+                Config.getPref().put("osm-server.url", "https://invalid.url");
+            }
+        }
+
+        @Override
+        public void beforeAll(ExtensionContext context) throws Exception {
+            if (!AnnotationSupport.isAnnotated(context.getElement(), BasicPreferences.class)) {
+                fail("OsmApiExtension requires @BasicPreferences");
+            }
+            super.beforeAll(context);
+            Config.getPref().put("osm-server.url", getWiremock(context).baseUrl());
+            OsmApi.getOsmApi().initialize(NullProgressMonitor.INSTANCE);
+        }
+    }
+}
diff --git a/test/unit/org/openstreetmap/josm/testutils/annotations/HTTP.java b/test/unit/org/openstreetmap/josm/testutils/annotations/HTTP.java
new file mode 100644
index 0000000000..d3a49dba63
--- /dev/null
+++ b/test/unit/org/openstreetmap/josm/testutils/annotations/HTTP.java
@@ -0,0 +1,63 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.testutils.annotations;
+
+import static java.lang.annotation.ElementType.TYPE;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+import java.net.URL;
+import java.util.Optional;
+
+import org.junit.jupiter.api.extension.AfterAllCallback;
+import org.junit.jupiter.api.extension.BeforeAllCallback;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.junit.jupiter.api.extension.ExtensionContext;
+import org.junit.platform.commons.support.AnnotationSupport;
+import org.junit.platform.commons.support.ReflectionSupport;
+import org.openstreetmap.josm.tools.Http1Client;
+import org.openstreetmap.josm.tools.HttpClient;
+
+/**
+ * Set up the HttpClient factory
+ * @author Taylor Smock
+ * @since xxx
+ */
+@Documented
+@Retention(RUNTIME)
+@Target(TYPE)
+@ExtendWith(HTTP.HTTPExtension.class)
+public @interface HTTP {
+    /**
+     * Set the HttpClient type
+     * Note that {@link HttpClient#HttpClient(URL, String)} must be accessible.
+     *
+     * @return The client type to create
+     */
+    Class<? extends HttpClient> value() default Http1Client.class;
+
+    /**
+     * Initialize and reset HttpClient
+     * @author Taylor Smock
+     *
+     */
+    class HTTPExtension implements BeforeAllCallback, AfterAllCallback {
+        @Override
+        public void afterAll(ExtensionContext context) throws Exception {
+            AnnotationUtils.resetStaticClass(HttpClient.class);
+        }
+
+        @Override
+        public void beforeAll(ExtensionContext context) throws Exception {
+            final Class<? extends HttpClient> clientFactory;
+            final Optional<HTTP> annotation = AnnotationSupport.findAnnotation(context.getElement(), HTTP.class);
+            if (annotation.isPresent()) {
+                clientFactory = annotation.get().value();
+            } else {
+                clientFactory = Http1Client.class;
+            }
+            HttpClient.setFactory((url, method) -> ReflectionSupport.newInstance(clientFactory, url, method));
+        }
+    }
+}
diff --git a/test/unit/org/openstreetmap/josm/tools/bugreport/BugReportSenderTest.java b/test/unit/org/openstreetmap/josm/tools/bugreport/BugReportSenderTest.java
index d12913413e..3ce532dade 100644
--- a/test/unit/org/openstreetmap/josm/tools/bugreport/BugReportSenderTest.java
+++ b/test/unit/org/openstreetmap/josm/tools/bugreport/BugReportSenderTest.java
@@ -7,53 +7,45 @@ import static com.github.tomakehurst.wiremock.client.WireMock.exactly;
 import static com.github.tomakehurst.wiremock.client.WireMock.post;
 import static com.github.tomakehurst.wiremock.client.WireMock.postRequestedFor;
 import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo;
-import static com.github.tomakehurst.wiremock.client.WireMock.verify;
-import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
 
 import java.net.URI;
 import java.util.List;
 
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.openstreetmap.josm.JOSMFixture;
+import org.junit.jupiter.api.Test;
 import org.openstreetmap.josm.actions.ShowStatusReportAction;
 import org.openstreetmap.josm.spi.preferences.Config;
+import org.openstreetmap.josm.testutils.annotations.BasicPreferences;
+import org.openstreetmap.josm.testutils.annotations.BasicWiremock;
+import org.openstreetmap.josm.testutils.annotations.HTTP;
 import org.openstreetmap.josm.testutils.mockers.OpenBrowserMocker;
 
-import com.github.tomakehurst.wiremock.junit.WireMockRule;
+import com.github.tomakehurst.wiremock.WireMockServer;
 
 /**
  * Unit tests of {@link BugReportSender} class.
  */
-public class BugReportSenderTest {
-
-    /**
-     * Setup tests.
-     */
-    @Before
-    public void setUp() {
-        JOSMFixture.createUnitTestFixture().init();
-    }
-
+@BasicPreferences
+@BasicWiremock
+@HTTP
+class BugReportSenderTest {
     /**
-     * HTTP mock.
+     * HTTP mock
      */
-    @Rule
-    public WireMockRule wireMockRule = new WireMockRule(options().dynamicPort());
+    @BasicWiremock
+    WireMockServer wireMockServer;
 
     /**
      * Unit test for {@link BugReportSender#BugReportSender}.
      * @throws InterruptedException if the thread is interrupted
      */
     @Test
-    public void testBugReportSender() throws InterruptedException {
-        Config.getPref().put("josm.url", wireMockRule.baseUrl());
-        wireMockRule.stubFor(post(urlEqualTo("/josmticket"))
+    void testBugReportSender() throws InterruptedException {
+        Config.getPref().put("josm.url", wireMockServer.baseUrl());
+        wireMockServer.stubFor(post(urlEqualTo("/josmticket"))
                 .willReturn(aResponse()
                         .withStatus(200)
                         .withHeader("Content-Type", "text/xml")
@@ -73,10 +65,10 @@ public class BugReportSenderTest {
 
         assertFalse(sender.isAlive());
         assertNull(sender.getErrorMessage(), sender.getErrorMessage());
-        verify(exactly(1), postRequestedFor(urlEqualTo("/josmticket")).withRequestBody(containing("pdata=")));
+        wireMockServer.verify(exactly(1), postRequestedFor(urlEqualTo("/josmticket")).withRequestBody(containing("pdata=")));
 
         List<URI> calledURIs = OpenBrowserMocker.getCalledURIs();
         assertEquals(1, calledURIs.size());
-        assertEquals(wireMockRule.url("/josmticket?pdata_stored=6bccff5c0417217bfbbe5fff"), calledURIs.get(0).toString());
+        assertEquals(wireMockServer.url("/josmticket?pdata_stored=6bccff5c0417217bfbbe5fff"), calledURIs.get(0).toString());
     }
 }
