Index: /trunk/src/org/openstreetmap/josm/data/imagery/JosmTemplatedTMSTileSource.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/data/imagery/JosmTemplatedTMSTileSource.java	(revision 18765)
+++ /trunk/src/org/openstreetmap/josm/data/imagery/JosmTemplatedTMSTileSource.java	(revision 18766)
@@ -19,4 +19,7 @@
     public JosmTemplatedTMSTileSource(TileSourceInfo info) {
         super(info);
+        if (info instanceof ImageryInfo) {
+            getHeaders().putAll(((ImageryInfo) info).getCustomHttpHeaders());
+        }
     }
 
Index: /trunk/test/unit/org/openstreetmap/josm/data/imagery/JosmTemplatedTMSTileSourceTest.java
===================================================================
--- /trunk/test/unit/org/openstreetmap/josm/data/imagery/JosmTemplatedTMSTileSourceTest.java	(revision 18766)
+++ /trunk/test/unit/org/openstreetmap/josm/data/imagery/JosmTemplatedTMSTileSourceTest.java	(revision 18766)
@@ -0,0 +1,23 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.data.imagery;
+
+import org.openstreetmap.gui.jmapviewer.interfaces.TemplatedTileSource;
+
+/**
+ * Test class for {@link JosmTemplatedTMSTileSource}
+ */
+class JosmTemplatedTMSTileSourceTest implements TileSourceTest {
+    @Override
+    public ImageryInfo getInfo() {
+        final ImageryInfo info = new ImageryInfo();
+        info.setImageryType(ImageryInfo.ImageryType.TMS);
+        // This is a temporary extended URL. Set it to the wiremock server if one is added and used.
+        info.setExtendedUrl("https://localhost:8111/example/{x}/{y}/{z}.png");
+        return info;
+    }
+
+    @Override
+    public TemplatedTileSource getTileSource(ImageryInfo info) {
+        return new JosmTemplatedTMSTileSource(info);
+    }
+}
Index: /trunk/test/unit/org/openstreetmap/josm/data/imagery/TemplatedWMSTileSourceTest.java
===================================================================
--- /trunk/test/unit/org/openstreetmap/josm/data/imagery/TemplatedWMSTileSourceTest.java	(revision 18765)
+++ /trunk/test/unit/org/openstreetmap/josm/data/imagery/TemplatedWMSTileSourceTest.java	(revision 18766)
@@ -8,8 +8,8 @@
 
 import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.RegisterExtension;
 import org.openstreetmap.gui.jmapviewer.FeatureAdapter;
 import org.openstreetmap.gui.jmapviewer.TileXY;
 import org.openstreetmap.gui.jmapviewer.interfaces.ICoordinate;
+import org.openstreetmap.gui.jmapviewer.interfaces.TemplatedTileSource;
 import org.openstreetmap.gui.jmapviewer.tilesources.TemplatedTMSTileSource;
 import org.openstreetmap.josm.data.Bounds;
@@ -20,22 +20,23 @@
 import org.openstreetmap.josm.data.projection.ProjectionRegistry;
 import org.openstreetmap.josm.data.projection.Projections;
-import org.openstreetmap.josm.testutils.JOSMTestRules;
-
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
 
 /**
  * Unit tests for class {@link TemplatedWMSTileSource}.
  */
-class TemplatedWMSTileSourceTest {
+@org.openstreetmap.josm.testutils.annotations.Projection
+class TemplatedWMSTileSourceTest implements TileSourceTest {
 
     private final ImageryInfo testImageryWMS = new ImageryInfo("test imagery", "http://localhost", "wms", null, null);
     private final ImageryInfo testImageryTMS = new ImageryInfo("test imagery", "http://localhost", "tms", null, null);
 
-    /**
-     * Setup test.
-     */
-    @RegisterExtension
-    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
-    public JOSMTestRules test = new JOSMTestRules();
+    @Override
+    public ImageryInfo getInfo() {
+        return new ImageryInfo(testImageryWMS);
+    }
+
+    @Override
+    public TemplatedTileSource getTileSource(ImageryInfo info) {
+        return new TemplatedWMSTileSource(info, ProjectionRegistry.getProjection());
+    }
 
     /**
Index: /trunk/test/unit/org/openstreetmap/josm/data/imagery/TileSourceTest.java
===================================================================
--- /trunk/test/unit/org/openstreetmap/josm/data/imagery/TileSourceTest.java	(revision 18766)
+++ /trunk/test/unit/org/openstreetmap/josm/data/imagery/TileSourceTest.java	(revision 18766)
@@ -0,0 +1,38 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.data.imagery;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import java.util.Collections;
+
+import org.junit.jupiter.api.Test;
+import org.openstreetmap.gui.jmapviewer.interfaces.TemplatedTileSource;
+
+/**
+ * Interface for tests common between {@link org.openstreetmap.gui.jmapviewer.interfaces.TemplatedTileSource} classes.
+ */
+public interface TileSourceTest {
+    /**
+     * Get a generic test info for "general" tests. <i>This will be modified!</i>
+     * @return The info to use for testing
+     */
+    ImageryInfo getInfo();
+
+    /**
+     * Get the tile source for a test
+     * @param info The info to use
+     * @return The tilesource
+     */
+    TemplatedTileSource getTileSource(ImageryInfo info);
+
+    /**
+     * Ensure that custom headers are set
+     */
+    @Test
+    default void testCustomHeaders() {
+        final ImageryInfo info = getInfo();
+        info.setCustomHttpHeaders(Collections.singletonMap("test_header", "is here"));
+        final TemplatedTileSource tileSource = getTileSource(info);
+        assertEquals("is here", tileSource.getHeaders().get("test_header"));
+    }
+}
Index: /trunk/test/unit/org/openstreetmap/josm/data/imagery/WMSEndpointTileSourceTest.java
===================================================================
--- /trunk/test/unit/org/openstreetmap/josm/data/imagery/WMSEndpointTileSourceTest.java	(revision 18765)
+++ /trunk/test/unit/org/openstreetmap/josm/data/imagery/WMSEndpointTileSourceTest.java	(revision 18766)
@@ -2,13 +2,15 @@
 package org.openstreetmap.josm.data.imagery;
 
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 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.Arrays;
+import java.util.Collections;
 
 import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.RegisterExtension;
+import org.openstreetmap.gui.jmapviewer.interfaces.TemplatedTileSource;
 import org.openstreetmap.josm.TestUtils;
 import org.openstreetmap.josm.data.imagery.ImageryInfo.ImageryType;
@@ -16,5 +18,4 @@
 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;
 
@@ -22,17 +23,36 @@
 import com.github.tomakehurst.wiremock.client.WireMock;
 
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+import org.openstreetmap.josm.testutils.annotations.Projection;
 
 @BasicWiremock
-class WMSEndpointTileSourceTest {
-    /**
-     * Setup test
-     */
-    @RegisterExtension
-    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
-    public JOSMTestRules test = new JOSMTestRules().projection();
-
+@Projection
+class WMSEndpointTileSourceTest implements TileSourceTest {
     @BasicWiremock
     WireMockServer tileServer;
+
+    private void basicMock() {
+        final byte[] response = assertDoesNotThrow(() -> Files.readAllBytes(
+                Paths.get(TestUtils.getTestDataRoot() + "wms/geofabrik-osm-inspector.xml")));
+        tileServer.stubFor(
+                WireMock.get(WireMock.urlEqualTo("/capabilities?SERVICE=WMS&REQUEST=GetCapabilities"))
+                        .willReturn(WireMock.aResponse().withBody(response))
+        );
+    }
+
+    @Override
+    public ImageryInfo getInfo() {
+        this.basicMock();
+        final ImageryInfo info = new ImageryInfo("WMSEndpointTileSourceTest");
+        info.setExtendedUrl(tileServer.url("/capabilities"));
+        info.setDefaultLayers(Collections.singletonList(new DefaultLayer(ImageryType.WMS_ENDPOINT,
+                "single_node_in_way", "default", null)));
+        info.setImageryType(ImageryType.WMS_ENDPOINT);
+        return info;
+    }
+
+    @Override
+    public TemplatedTileSource getTileSource(ImageryInfo info) {
+        return new WMSEndpointTileSource(info, ProjectionRegistry.getProjection());
+    }
 
     @Test
@@ -73,5 +93,5 @@
                 )));
 
-        Config.getPref().putList("imagery.layers.sites", Arrays.asList(tileServer.url("//maps")));
+        Config.getPref().putList("imagery.layers.sites", Collections.singletonList(tileServer.url("//maps")));
         ImageryLayerInfo.instance.loadDefaults(true, null, false);
         assertEquals(1, ImageryLayerInfo.instance.getDefaultLayers().size());
@@ -87,5 +107,5 @@
 
     @Test
-    void testCustomHeaders() throws Exception {
+    void testCustomHeadersServerSide() throws IOException {
         tileServer.stubFor(
                 WireMock.get(WireMock.urlEqualTo("/capabilities?SERVICE=WMS&REQUEST=GetCapabilities"))
@@ -118,8 +138,8 @@
                 )));
 
-        Config.getPref().putList("imagery.layers.sites", Arrays.asList(tileServer.url("//maps")));
+        Config.getPref().putList("imagery.layers.sites", Collections.singletonList(tileServer.url("//maps")));
         ImageryLayerInfo.instance.loadDefaults(true, null, false);
         ImageryInfo wmsImageryInfo = ImageryLayerInfo.instance.getDefaultLayers().get(0);
-        wmsImageryInfo.setDefaultLayers(Arrays.asList(new DefaultLayer(ImageryType.WMS_ENDPOINT, "historiske-ortofoto", "", "")));
+        wmsImageryInfo.setDefaultLayers(Collections.singletonList(new DefaultLayer(ImageryType.WMS_ENDPOINT, "historiske-ortofoto", "", "")));
         WMSEndpointTileSource tileSource = new WMSEndpointTileSource(wmsImageryInfo, ProjectionRegistry.getProjection());
         tileSource.initProjection(Projections.getProjectionByCode("EPSG:3857"));
Index: /trunk/test/unit/org/openstreetmap/josm/data/imagery/WMTSTileSourceTest.java
===================================================================
--- /trunk/test/unit/org/openstreetmap/josm/data/imagery/WMTSTileSourceTest.java	(revision 18765)
+++ /trunk/test/unit/org/openstreetmap/josm/data/imagery/WMTSTileSourceTest.java	(revision 18766)
@@ -25,9 +25,8 @@
 import com.github.tomakehurst.wiremock.WireMockServer;
 import com.github.tomakehurst.wiremock.client.WireMock;
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
 import org.apache.commons.io.FileUtils;
 import org.junit.jupiter.api.Disabled;
 import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.RegisterExtension;
+import org.junit.jupiter.api.Timeout;
 import org.junit.jupiter.api.io.TempDir;
 import org.junit.jupiter.params.ParameterizedTest;
@@ -44,7 +43,7 @@
 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 org.openstreetmap.josm.testutils.annotations.Projection;
 import org.openstreetmap.josm.tools.ReflectionUtils;
 
@@ -54,13 +53,7 @@
 @BasicWiremock
 @BasicPreferences
+@Projection
+@Timeout(value = 5, unit = TimeUnit.MINUTES)
 class WMTSTileSourceTest {
-
-    /**
-     * Setup test.
-     */
-    @RegisterExtension
-    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
-    public static JOSMTestRules test = new JOSMTestRules().projection().timeout((int) TimeUnit.MINUTES.toMillis(5));
-
     @BasicWiremock
     WireMockServer tileServer;
Index: /trunk/test/unit/org/openstreetmap/josm/data/imagery/vectortile/mapbox/MapboxVectorTileSourceTest.java
===================================================================
--- /trunk/test/unit/org/openstreetmap/josm/data/imagery/vectortile/mapbox/MapboxVectorTileSourceTest.java	(revision 18765)
+++ /trunk/test/unit/org/openstreetmap/josm/data/imagery/vectortile/mapbox/MapboxVectorTileSourceTest.java	(revision 18766)
@@ -6,16 +6,5 @@
 import static org.junit.jupiter.api.Assertions.assertNull;
 
-
 import java.util.stream.Stream;
-
-import org.junit.jupiter.api.extension.RegisterExtension;
-import org.openstreetmap.josm.TestUtils;
-import org.openstreetmap.josm.data.imagery.ImageryInfo;
-import org.openstreetmap.josm.data.imagery.vectortile.mapbox.style.MapboxVectorStyle;
-import org.openstreetmap.josm.data.imagery.vectortile.mapbox.style.Source;
-import org.openstreetmap.josm.gui.ExtendedDialog;
-import org.openstreetmap.josm.gui.widgets.JosmComboBox;
-import org.openstreetmap.josm.testutils.JOSMTestRules;
-import org.openstreetmap.josm.testutils.mockers.ExtendedDialogMocker;
 
 import org.junit.jupiter.api.Test;
@@ -23,4 +12,12 @@
 import org.junit.jupiter.params.provider.Arguments;
 import org.junit.jupiter.params.provider.MethodSource;
+import org.openstreetmap.josm.TestUtils;
+import org.openstreetmap.josm.data.imagery.ImageryInfo;
+import org.openstreetmap.josm.data.imagery.TileSourceTest;
+import org.openstreetmap.josm.data.imagery.vectortile.mapbox.style.MapboxVectorStyle;
+import org.openstreetmap.josm.data.imagery.vectortile.mapbox.style.Source;
+import org.openstreetmap.josm.gui.ExtendedDialog;
+import org.openstreetmap.josm.gui.widgets.JosmComboBox;
+import org.openstreetmap.josm.testutils.mockers.ExtendedDialogMocker;
 
 /**
@@ -29,7 +26,5 @@
  * @since 17862
  */
-class MapboxVectorTileSourceTest {
-    @RegisterExtension
-    JOSMTestRules rule = new JOSMTestRules();
+class MapboxVectorTileSourceTest implements TileSourceTest {
     private static class SelectLayerDialogMocker extends ExtendedDialogMocker {
         int index;
@@ -45,8 +40,17 @@
     }
 
+    @Override
+    public ImageryInfo getInfo() {
+        return new ImageryInfo("Test Mapillary", "file:/" + TestUtils.getTestDataRoot() + "pbf/mapillary/{z}/{x}/{y}.mvt");
+    }
+
+    @Override
+    public MapboxVectorTileSource getTileSource(ImageryInfo info) {
+        return new MapboxVectorTileSource(info);
+    }
+
     @Test
     void testNoStyle() {
-        MapboxVectorTileSource tileSource = new MapboxVectorTileSource(
-          new ImageryInfo("Test Mapillary", "file:/" + TestUtils.getTestDataRoot() + "pbf/mapillary/{z}/{x}/{y}.mvt"));
+        MapboxVectorTileSource tileSource = getTileSource(getInfo());
         assertNull(tileSource.getStyleSource());
     }
@@ -69,5 +73,5 @@
         extendedDialogMocker.index = index;
         extendedDialogMocker.getMockResultMap().put(dialogMockerText, "Add layers");
-        MapboxVectorTileSource tileSource = new MapboxVectorTileSource(
+        MapboxVectorTileSource tileSource = getTileSource(
           new ImageryInfo("Test Mapillary", "file:/" + TestUtils.getTestDataRoot() + "mapillary.json"));
         MapboxVectorStyle styleSource = tileSource.getStyleSource();
