diff --git a/core/src/main/java/org/mapfish/print/map/geotools/FeaturesParser.java b/core/src/main/java/org/mapfish/print/map/geotools/FeaturesParser.java
index 9d84a43b91..13b8df38a9 100644
--- a/core/src/main/java/org/mapfish/print/map/geotools/FeaturesParser.java
+++ b/core/src/main/java/org/mapfish/print/map/geotools/FeaturesParser.java
@@ -61,7 +61,7 @@ public FeaturesParser(
   }
 
   @VisibleForTesting
-  static final CoordinateReferenceSystem parseCoordinateReferenceSystem(
+  static CoordinateReferenceSystem parseCoordinateReferenceSystem(
       final MfClientHttpRequestFactory requestFactory,
       final JSONObject geojson,
       final boolean forceLongitudeFirst) {
@@ -108,7 +108,8 @@ static final CoordinateReferenceSystem parseCoordinateReferenceSystem(
               }
             }
           } else {
-            LOGGER.warn("Unable to load linked CRS from geojson: \n{}", crsJson);
+            LOGGER.warn(
+                "Unsupported link type {} in linked CRS from geojson: \n{}", linkType, crsJson);
           }
         } else {
           code.append(getProperty(crsJson, "code"));
@@ -147,7 +148,6 @@ private static String getProperty(final JSONObject crsJson, final String nameCod
    * @param template the template
    * @param features what to parse
    * @return the feature collection
-   * @throws IOException
    */
   public final SimpleFeatureCollection autoTreat(final Template template, final String features)
       throws IOException {
@@ -189,7 +189,6 @@ public final SimpleFeatureCollection treatStringAsURL(
    *
    * @param geoJsonString what to parse
    * @return the feature collection
-   * @throws IOException
    */
   public final SimpleFeatureCollection treatStringAsGeoJson(final String geoJsonString)
       throws IOException {
@@ -231,7 +230,7 @@ private SimpleFeatureType createFeatureType(@Nonnull final String geojsonData) {
         builder.setName("GeosjonFeatureType");
         final JSONArray features = geojson.getJSONArray("features");
 
-        if (features.length() == 0) {
+        if (features.isEmpty()) {
           // do not try to build the feature type if there are no features
           return null;
         }
diff --git a/core/src/main/java/org/mapfish/print/map/style/ParserPluginUtils.java b/core/src/main/java/org/mapfish/print/map/style/ParserPluginUtils.java
index d965fabb8d..4fc4635c5e 100644
--- a/core/src/main/java/org/mapfish/print/map/style/ParserPluginUtils.java
+++ b/core/src/main/java/org/mapfish/print/map/style/ParserPluginUtils.java
@@ -34,7 +34,7 @@ public static Optional<Style> loadStyleAsURI(
       final ClientHttpRequestFactory clientHttpRequestFactory,
       final String styleRef,
       final Function<byte[], @Nullable Optional<Style>> loadFunction) {
-    int statusCode;
+    int httpStatusCode;
     final byte[] input;
 
     URI uri;
@@ -47,13 +47,13 @@ public static Optional<Style> loadStyleAsURI(
     try {
       final ClientHttpRequest request = clientHttpRequestFactory.createRequest(uri, HttpMethod.GET);
       try (ClientHttpResponse response = request.execute()) {
-        statusCode = response.getRawStatusCode();
+        httpStatusCode = response.getRawStatusCode();
         input = IOUtils.toByteArray(response.getBody());
       }
     } catch (Exception e) {
       return Optional.empty();
     }
-    if (statusCode == HttpStatus.OK.value()) {
+    if (httpStatusCode == HttpStatus.OK.value()) {
       return loadFunction.apply(input);
     } else {
       return Optional.empty();
diff --git a/core/src/main/java/org/mapfish/print/processor/jasper/HttpImageResolver.java b/core/src/main/java/org/mapfish/print/processor/jasper/HttpImageResolver.java
index f8a5987e48..5bc6ee6fd5 100644
--- a/core/src/main/java/org/mapfish/print/processor/jasper/HttpImageResolver.java
+++ b/core/src/main/java/org/mapfish/print/processor/jasper/HttpImageResolver.java
@@ -1,5 +1,6 @@
 package org.mapfish.print.processor.jasper;
 
+import com.google.common.annotations.VisibleForTesting;
 import java.awt.image.BufferedImage;
 import java.io.IOException;
 import java.net.URI;
@@ -29,7 +30,7 @@ public final class HttpImageResolver implements TableColumnConverter<BufferedIma
   private static final int IMAGE_SIZE = 48;
   private Pattern urlExtractor = Pattern.compile("(.*)");
   private int urlGroup = 1;
-  private final BufferedImage defaultImage;
+  @VisibleForTesting final BufferedImage defaultImage;
 
   /** Constructor. */
   public HttpImageResolver() {
diff --git a/core/src/main/java/org/mapfish/print/processor/jasper/LegendProcessor.java b/core/src/main/java/org/mapfish/print/processor/jasper/LegendProcessor.java
index f6c17d4629..8ff512b78a 100644
--- a/core/src/main/java/org/mapfish/print/processor/jasper/LegendProcessor.java
+++ b/core/src/main/java/org/mapfish/print/processor/jasper/LegendProcessor.java
@@ -2,6 +2,7 @@
 
 import com.codahale.metrics.MetricRegistry;
 import com.codahale.metrics.Timer;
+import com.google.common.annotations.VisibleForTesting;
 import java.awt.Color;
 import java.awt.Dimension;
 import java.awt.Graphics2D;
@@ -161,7 +162,14 @@ private void createTasks(
     if (icons != null && icons.length > 0) {
       for (URL icon : icons) {
         tasks.add(
-            new IconTask(icon, dpi, context, level, tempTaskDirectory, clientHttpRequestFactory));
+            new IconTask(
+                icon,
+                dpi,
+                context,
+                level,
+                tempTaskDirectory,
+                clientHttpRequestFactory,
+                metricRegistry));
       }
     }
     if (legendAttributes.classes != null) {
@@ -269,7 +277,8 @@ protected void extraValidation(
     // no checks needed
   }
 
-  private synchronized BufferedImage getMissingImage() {
+  @VisibleForTesting
+  synchronized BufferedImage getMissingImage() {
     if (this.missingImage == null) {
       this.missingImage =
           new BufferedImage(
@@ -311,7 +320,7 @@ public static final class Output {
     /** The datasource for the legend object in the report. */
     public final JRTableModelDataSource legendDataSource;
 
-    /** The path to the compiled subreport. */
+    /** The path to the compiled sub-report. */
     public final String legendSubReport;
 
     /** The number of rows in the legend. */
@@ -343,7 +352,8 @@ public Object[] call() {
     }
   }
 
-  private final class IconTask implements Callable<Object[]> {
+  @VisibleForTesting
+  final class IconTask implements Callable<Object[]> {
 
     private final URL icon;
     private final double iconDPI;
@@ -351,20 +361,24 @@ private final class IconTask implements Callable<Object[]> {
     private final MfClientHttpRequestFactory clientHttpRequestFactory;
     private final int level;
     private final File tempTaskDirectory;
+    private final MetricRegistry metricRegistry;
 
-    private IconTask(
+    @VisibleForTesting
+    IconTask(
         final URL icon,
         final double iconDPI,
         final ExecutionContext context,
         final int level,
         final File tempTaskDirectory,
-        final MfClientHttpRequestFactory clientHttpRequestFactory) {
+        final MfClientHttpRequestFactory clientHttpRequestFactory,
+        final MetricRegistry metricRegistry) {
       this.icon = icon;
       this.iconDPI = iconDPI;
       this.context = context;
       this.level = level;
       this.clientHttpRequestFactory = clientHttpRequestFactory;
       this.tempTaskDirectory = tempTaskDirectory;
+      this.metricRegistry = metricRegistry;
     }
 
     @Override
@@ -389,8 +403,7 @@ private BufferedImage loadImage(final URI uri) {
           this.context.stopIfCanceled();
           final ClientHttpRequest request =
               this.clientHttpRequestFactory.createRequest(uri, HttpMethod.GET);
-          try (Timer.Context ignored =
-              LegendProcessor.this.metricRegistry.timer(metricName).time()) {
+          try (Timer.Context ignored = metricRegistry.timer(metricName).time()) {
             try (ClientHttpResponse httpResponse = request.execute()) {
               if (httpResponse.getRawStatusCode() == HttpStatus.OK.value()) {
                 image = ImageIO.read(httpResponse.getBody());
@@ -409,7 +422,7 @@ private BufferedImage loadImage(final URI uri) {
 
       if (image == null) {
         image = getMissingImage();
-        LegendProcessor.this.metricRegistry.counter(metricName + ".error").inc();
+        metricRegistry.counter(metricName + ".error").inc();
       }
 
       return image;
diff --git a/core/src/test/java/org/mapfish/print/map/geotools/FeaturesParserTest.java b/core/src/test/java/org/mapfish/print/map/geotools/FeaturesParserTest.java
index 1b7f505464..5ef8a8f7d8 100644
--- a/core/src/test/java/org/mapfish/print/map/geotools/FeaturesParserTest.java
+++ b/core/src/test/java/org/mapfish/print/map/geotools/FeaturesParserTest.java
@@ -3,6 +3,7 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertSame;
 import static org.junit.Assert.assertTrue;
 
 import java.io.File;
@@ -36,6 +37,10 @@ public class FeaturesParserTest extends AbstractMapfishSpringTest {
 
   private static final String EXAMPLE_GEOJSONFILE =
       "geojson/geojson-inconsistent-attributes-2.json";
+  private static final String WKT =
+      "GEOGCS[\"GCS_WGS_1984\",DATUM[\"D_WGS_1984\""
+          + ",SPHEROID[\"WGS_1984\",6378137,298.257223563]]"
+          + ",PRIMEM[\"Greenwich\",0],UNIT[\"Degree\",0.017453292519943295]]";
   @Autowired private TestHttpClientFactory requestFactory;
   @Autowired private ConfigurationFactory configurationFactory;
 
@@ -131,19 +136,32 @@ public MockClientHttpRequest handleRequest(URI uri, HttpMethod httpMethod) {
   @Test
   @DirtiesContext
   public void testParseCRSLinkEsriWkt() {
+    final MockClientHttpResponse clientHttpResponse =
+        new MockClientHttpResponse(WKT.getBytes(Constants.DEFAULT_CHARSET), HttpStatus.OK);
+
+    CoordinateReferenceSystem crs = parseCoordinateReferenceSystemFromResponse(clientHttpResponse);
+    assertNotSame(DefaultEngineeringCRS.GENERIC_2D, crs);
+  }
+
+  @Test
+  @DirtiesContext
+  public void testParseCRSLinkEsriWktWithUnsupportedErrorCode() {
+    final MockClientHttpResponse clientHttpResponse =
+        new MockClientHttpResponse(WKT.getBytes(Constants.DEFAULT_CHARSET), 999);
+
+    CoordinateReferenceSystem crs = parseCoordinateReferenceSystemFromResponse(clientHttpResponse);
+    assertSame(DefaultEngineeringCRS.GENERIC_2D, crs);
+  }
+
+  private CoordinateReferenceSystem parseCoordinateReferenceSystemFromResponse(
+      final MockClientHttpResponse clientHttpResponse) {
     requestFactory.registerHandler(
         input -> input != null && input.getHost().equals("spatialreference.org"),
         new TestHttpClientFactory.Handler() {
           @Override
           public MockClientHttpRequest handleRequest(URI uri, HttpMethod httpMethod) {
-            String wkt =
-                "GEOGCS[\"GCS_WGS_1984\",DATUM[\"D_WGS_1984\",SPHEROID[\"WGS_1984\",6378137,298"
-                    + ".257223563]],"
-                    + "PRIMEM[\"Greenwich\","
-                    + "0],UNIT[\"Degree\",0.017453292519943295]]";
             MockClientHttpRequest mockClientHttpRequest = new MockClientHttpRequest();
-            mockClientHttpRequest.setResponse(
-                new MockClientHttpResponse(wkt.getBytes(Constants.DEFAULT_CHARSET), HttpStatus.OK));
+            mockClientHttpRequest.setResponse(clientHttpResponse);
             return mockClientHttpRequest;
           }
         });
@@ -161,10 +179,11 @@ public MockClientHttpRequest handleRequest(URI uri, HttpMethod httpMethod) {
     CoordinateReferenceSystem crs =
         FeaturesParser.parseCoordinateReferenceSystem(this.requestFactory, crsJSON, false);
     assertNotNull(crs);
-    assertNotSame(DefaultEngineeringCRS.GENERIC_2D, crs);
+    return crs;
   }
 
-  public void testParseCRSLinkProj4() {
+  @Test
+  public void testUnsupportedLinkTypeProj4() {
     JSONObject crsJSON =
         new JSONObject(
             "{\"crs\":{\n"
@@ -179,7 +198,7 @@ public void testParseCRSLinkProj4() {
     CoordinateReferenceSystem crs =
         FeaturesParser.parseCoordinateReferenceSystem(this.requestFactory, crsJSON, false);
     assertNotNull(crs);
-    assertNotSame(DefaultEngineeringCRS.GENERIC_2D, crs);
+    assertSame(DefaultEngineeringCRS.GENERIC_2D, crs);
   }
 
   @Test
diff --git a/core/src/test/java/org/mapfish/print/map/image/AbstractSingleImageLayerTest.java b/core/src/test/java/org/mapfish/print/map/image/AbstractSingleImageLayerTest.java
new file mode 100644
index 0000000000..0be68f680d
--- /dev/null
+++ b/core/src/test/java/org/mapfish/print/map/image/AbstractSingleImageLayerTest.java
@@ -0,0 +1,56 @@
+package org.mapfish.print.map.image;
+
+import static org.junit.Assert.assertTrue;
+
+import com.codahale.metrics.MetricRegistry;
+import java.awt.image.BufferedImage;
+import java.io.IOException;
+import org.junit.Assert;
+import org.junit.Test;
+import org.mapfish.print.Constants;
+import org.mapfish.print.attribute.map.MapfishMapContext;
+import org.mapfish.print.http.MfClientHttpRequestFactory;
+import org.mapfish.print.map.AbstractLayerParams;
+import org.springframework.mock.http.client.MockClientHttpRequest;
+import org.springframework.mock.http.client.MockClientHttpResponse;
+
+public class AbstractSingleImageLayerTest {
+
+  @Test
+  public void testFetchImage() throws IOException {
+    MapfishMapContext mapContext = new MapfishMapContext(null, null, 100, 100, false, true);
+    MockClientHttpRequest mockClientHttpRequest = new MockClientHttpRequest();
+    final int unsupportedStatusCode = 999;
+    mockClientHttpRequest.setResponse(
+        new MockClientHttpResponse(
+            "SomeResponse".getBytes(Constants.DEFAULT_CHARSET), unsupportedStatusCode));
+    AbstractLayerParams layerParams = new AbstractLayerParams();
+    layerParams.failOnError = true;
+    AbstractSingleImageLayer layer = new AbstractSingleImageLayerTestImpl(layerParams);
+
+    try {
+      layer.fetchImage(mockClientHttpRequest, mapContext);
+      Assert.fail("Did not throw exception with unsupported status code");
+    } catch (RuntimeException e) {
+      assertTrue(e.getMessage().contains(String.valueOf(unsupportedStatusCode)));
+    }
+  }
+
+  private static class AbstractSingleImageLayerTestImpl extends AbstractSingleImageLayer {
+
+    public AbstractSingleImageLayerTestImpl(AbstractLayerParams layerParams) {
+      super(null, null, layerParams, new MetricRegistry(), null);
+    }
+
+    @Override
+    protected BufferedImage loadImage(
+        MfClientHttpRequestFactory requestFactory, MapfishMapContext transformer) {
+      return null;
+    }
+
+    @Override
+    public RenderType getRenderType() {
+      return null;
+    }
+  }
+}
diff --git a/core/src/test/java/org/mapfish/print/map/style/ParserPluginUtilsTest.java b/core/src/test/java/org/mapfish/print/map/style/ParserPluginUtilsTest.java
new file mode 100644
index 0000000000..2dfe7cd645
--- /dev/null
+++ b/core/src/test/java/org/mapfish/print/map/style/ParserPluginUtilsTest.java
@@ -0,0 +1,75 @@
+package org.mapfish.print.map.style;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.nio.charset.Charset;
+import java.util.Optional;
+import java.util.function.Function;
+import org.geotools.api.style.Style;
+import org.junit.Test;
+import org.springframework.http.HttpMethod;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.client.ClientHttpRequest;
+import org.springframework.http.client.ClientHttpRequestFactory;
+import org.springframework.http.client.ClientHttpResponse;
+
+public class ParserPluginUtilsTest {
+
+  @Test
+  public void loadStyleAsURITest_ValidURI() throws IOException, URISyntaxException {
+    int httpStatusCode = HttpStatus.OK.value();
+    Style style = mock(Style.class);
+    Optional<Style> actualStyle = testLoadingStyleWithStatusCode(style, httpStatusCode);
+
+    assertTrue(actualStyle.isPresent());
+    assertEquals(style, actualStyle.get());
+  }
+
+  private static Optional<Style> testLoadingStyleWithStatusCode(
+      final Style style, final int httpStatusCode) throws IOException, URISyntaxException {
+    ClientHttpRequestFactory factory = mock(ClientHttpRequestFactory.class);
+    ClientHttpRequest request = mock(ClientHttpRequest.class);
+    ClientHttpResponse response = mock(ClientHttpResponse.class);
+
+    Function<byte[], Optional<Style>> function = bytes -> Optional.of(style);
+
+    when(factory.createRequest(new URI("http://valid.uri"), HttpMethod.GET)).thenReturn(request);
+    when(request.execute()).thenReturn(response);
+    when(response.getRawStatusCode()).thenReturn(httpStatusCode);
+    when(response.getBody())
+        .thenReturn(
+            new ByteArrayInputStream(
+                "This is dummy style data".getBytes(Charset.defaultCharset())));
+
+    return ParserPluginUtils.loadStyleAsURI(factory, "http://valid.uri", function);
+  }
+
+  @Test
+  public void loadStyleAsURITest_InvalidStatusCode() throws IOException, URISyntaxException {
+    int httpStatusCode = 999;
+    Style style = mock(Style.class);
+    Optional<Style> actualStyle = testLoadingStyleWithStatusCode(style, httpStatusCode);
+
+    assertFalse(actualStyle.isPresent());
+  }
+
+  @Test
+  public void loadStyleAsURITest_InValidURI() {
+    ClientHttpRequestFactory factory = mock(ClientHttpRequestFactory.class);
+    Style style = mock(Style.class);
+
+    Function<byte[], Optional<Style>> function = bytes -> Optional.of(style);
+
+    Optional<Style> actualStyle =
+        ParserPluginUtils.loadStyleAsURI(factory, "invalid|uri", function);
+    assertTrue(actualStyle.isEmpty());
+  }
+}
diff --git a/core/src/test/java/org/mapfish/print/map/tiled/SingleTileLoaderTaskTest.java b/core/src/test/java/org/mapfish/print/map/tiled/SingleTileLoaderTaskTest.java
new file mode 100644
index 0000000000..41f1539c0d
--- /dev/null
+++ b/core/src/test/java/org/mapfish/print/map/tiled/SingleTileLoaderTaskTest.java
@@ -0,0 +1,78 @@
+package org.mapfish.print.map.tiled;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import com.codahale.metrics.MetricRegistry;
+import java.awt.image.BufferedImage;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.HashMap;
+import org.junit.Test;
+import org.mapfish.print.PrintException;
+import org.mapfish.print.processor.AbstractProcessor;
+import org.mapfish.print.processor.Processor;
+import org.springframework.http.client.ClientHttpRequest;
+import org.springframework.http.client.ClientHttpResponse;
+
+public class SingleTileLoaderTaskTest {
+
+  @Test
+  public void testCompute() throws IOException, URISyntaxException {
+    final int unsupportedStatusCode = 200;
+    ClientHttpRequest tileRequest = mock(ClientHttpRequest.class);
+    CoverageTask.SingleTileLoaderTask task = prepareTest(unsupportedStatusCode, tileRequest);
+
+    CoverageTask.Tile singleTile = task.compute();
+    assertNotNull(singleTile);
+    verify(tileRequest, times(3)).getURI();
+  }
+
+  private CoverageTask.SingleTileLoaderTask prepareTest(
+      int unsupportedStatusCode, ClientHttpRequest tileRequest)
+      throws IOException, URISyntaxException {
+    BufferedImage errorImage = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB);
+    Processor.ExecutionContext context = new AbstractProcessor.Context(new HashMap<>());
+    MetricRegistry registry = new MetricRegistry();
+
+    // mocking ClientHttpRequest
+    ClientHttpResponse clientHttpResponse = getMockResponse(unsupportedStatusCode);
+    when(tileRequest.execute()).thenReturn(clientHttpResponse);
+    when(tileRequest.getURI()).thenReturn(new URI("http://localhost"));
+
+    return new CoverageTask.SingleTileLoaderTask(
+        tileRequest, errorImage, 0, 0, true, registry, context);
+  }
+
+  @Test
+  public void testComputeWithUnsupportedStatusCode() throws IOException, URISyntaxException {
+    final int unsupportedStatusCode = 999;
+    ClientHttpRequest tileRequest = mock(ClientHttpRequest.class);
+    CoverageTask.SingleTileLoaderTask task = prepareTest(unsupportedStatusCode, tileRequest);
+
+    try {
+      task.compute();
+      fail("Did not throw exception with unsupported status code");
+    } catch (PrintException e) {
+      assertTrue(e.getCause().getMessage().contains(String.valueOf(unsupportedStatusCode)));
+      verify(tileRequest, times(4)).getURI();
+    }
+  }
+
+  private ClientHttpResponse getMockResponse(int rawStatusCode) throws IOException {
+    ClientHttpResponse response = mock(ClientHttpResponse.class);
+    when(response.getRawStatusCode()).thenReturn(rawStatusCode);
+    when(response.getStatusCode())
+        .thenThrow(new RuntimeException("Unsupported status code has no HttpStatus"));
+    when(response.getBody()).thenReturn(new ByteArrayInputStream(new byte[0]));
+    when(response.getHeaders()).thenReturn(new org.springframework.http.HttpHeaders());
+    return response;
+  }
+}
diff --git a/core/src/test/java/org/mapfish/print/processor/jasper/HttpImageResolverTest.java b/core/src/test/java/org/mapfish/print/processor/jasper/HttpImageResolverTest.java
new file mode 100644
index 0000000000..a8a5eda73d
--- /dev/null
+++ b/core/src/test/java/org/mapfish/print/processor/jasper/HttpImageResolverTest.java
@@ -0,0 +1,128 @@
+package org.mapfish.print.processor.jasper;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.awt.image.BufferedImage;
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.IOException;
+import java.net.URI;
+import java.nio.charset.Charset;
+import java.nio.file.Files;
+import org.junit.Test;
+import org.mapfish.print.AbstractMapfishSpringTest;
+import org.mapfish.print.http.MfClientHttpRequestFactory;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.MediaType;
+import org.springframework.mock.http.client.MockClientHttpRequest;
+import org.springframework.mock.http.client.MockClientHttpResponse;
+
+public class HttpImageResolverTest {
+  @Test
+  public void testResolve() throws IOException {
+    int statusCode = HttpStatus.OK.value();
+    HttpImageResolver httpImageResolver = new HttpImageResolver();
+
+    BufferedImage result = resolveImageWithStatusCode(statusCode, httpImageResolver);
+
+    // Assert that image is not the default one
+    assertNotEquals(httpImageResolver.defaultImage, result);
+  }
+
+  private BufferedImage resolveImageWithStatusCode(
+      int statusCode, HttpImageResolver httpImageResolver) throws IOException {
+    String testUriString = "https://test.com/image.png";
+    // Mock request factory and request
+    MfClientHttpRequestFactory requestFactoryMock = mock(MfClientHttpRequestFactory.class);
+    MockClientHttpRequest requestMock = new MockClientHttpRequest();
+    when(requestFactoryMock.createRequest(any(URI.class), any())).thenReturn(requestMock);
+
+    MockClientHttpResponse responseMock = buildMockResponse(statusCode);
+    try {
+      when(requestFactoryMock.createRequest(
+              new URI(testUriString), org.springframework.http.HttpMethod.GET))
+          .thenReturn(requestMock);
+      requestMock.setResponse(responseMock);
+    } catch (Exception e) {
+      fail("Exception thrown: " + e.getMessage());
+    }
+
+    httpImageResolver.setUrlExtractor("(.*)");
+    httpImageResolver.setUrlGroup(1);
+
+    return httpImageResolver.resolve(requestFactoryMock, testUriString);
+  }
+
+  @Test
+  public void testResolveWithUnsupportedStatus() throws IOException {
+    int statusCode = 999;
+    HttpImageResolver httpImageResolver = new HttpImageResolver();
+
+    BufferedImage result = resolveImageWithStatusCode(statusCode, httpImageResolver);
+
+    // Assert that image is the default one
+    assertEquals(httpImageResolver.defaultImage, result);
+  }
+
+  private MockClientHttpResponse buildMockResponse(int statusCode) throws IOException {
+    File file = AbstractMapfishSpringTest.getFile(this.getClass(), "/map-data/tiger-ny.png");
+    byte[] responseBody = Files.readAllBytes(file.toPath());
+    ByteArrayInputStream inputStream = new ByteArrayInputStream(responseBody);
+    MockClientHttpResponse responseMock = new MockClientHttpResponse(inputStream, statusCode);
+    responseMock.getHeaders().setContentType(MediaType.IMAGE_PNG);
+    return responseMock;
+  }
+
+  @Test
+  public void testInvalidURIResolve() {
+    String invalidUriString = "htp:/invalid";
+    MfClientHttpRequestFactory requestFactoryMock = mock(MfClientHttpRequestFactory.class);
+    HttpImageResolver httpImageResolver = new HttpImageResolver();
+    httpImageResolver.setUrlExtractor("(.*)");
+    httpImageResolver.setUrlGroup(1);
+
+    // Calls the actual method with an invalid image
+    BufferedImage result = httpImageResolver.resolve(requestFactoryMock, invalidUriString);
+
+    assertEquals(httpImageResolver.defaultImage, result);
+  }
+
+  @Test
+  public void testInvalidUrlGroupResolve() throws IOException {
+    String testUriString = "https://test.com/image.png";
+    MfClientHttpRequestFactory requestFactoryMock = mock(MfClientHttpRequestFactory.class);
+    MockClientHttpRequest requestMock = new MockClientHttpRequest();
+    when(requestFactoryMock.createRequest(any(URI.class), any())).thenReturn(requestMock);
+
+    byte[] responseBody = "Image bytes".getBytes(Charset.defaultCharset());
+    MockClientHttpResponse responseMock = new MockClientHttpResponse(responseBody, HttpStatus.OK);
+    responseMock.getHeaders().setContentType(MediaType.IMAGE_PNG);
+    try {
+      when(requestFactoryMock.createRequest(
+              new URI(testUriString), org.springframework.http.HttpMethod.GET))
+          .thenReturn(requestMock);
+      requestMock.setResponse(responseMock);
+    } catch (Exception e) {
+      fail("Exception thrown: " + e.getMessage());
+    }
+
+    HttpImageResolver httpImageResolver = new HttpImageResolver();
+    httpImageResolver.setUrlExtractor("(.*)");
+    int urlGroup = 2;
+    httpImageResolver.setUrlGroup(urlGroup); // Invalid group
+
+    try {
+      // Calls the actual method with an invalid group
+      httpImageResolver.resolve(requestFactoryMock, testUriString);
+      fail("Did not throw IndexOutOfBoundsException for unsupported group");
+    } catch (IndexOutOfBoundsException e) {
+      assertTrue(e.getMessage().contains("No group " + urlGroup));
+    }
+  }
+}
diff --git a/core/src/test/java/org/mapfish/print/processor/jasper/IconTaskTest.java b/core/src/test/java/org/mapfish/print/processor/jasper/IconTaskTest.java
new file mode 100644
index 0000000000..00f58b9a15
--- /dev/null
+++ b/core/src/test/java/org/mapfish/print/processor/jasper/IconTaskTest.java
@@ -0,0 +1,76 @@
+package org.mapfish.print.processor.jasper;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import com.codahale.metrics.MetricRegistry;
+import java.awt.image.BufferedImage;
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.nio.charset.Charset;
+import java.util.HashMap;
+import org.junit.Test;
+import org.mapfish.print.http.MfClientHttpRequestFactory;
+import org.mapfish.print.processor.AbstractProcessor;
+import org.mapfish.print.processor.Processor;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.MediaType;
+import org.springframework.mock.http.client.MockClientHttpRequest;
+import org.springframework.mock.http.client.MockClientHttpResponse;
+
+public class IconTaskTest {
+
+  @Test
+  public void call_iconImageDoesNotExist_returnsMissingImageWhenOk() throws Exception {
+    int statusCode = HttpStatus.OK.value();
+    BufferedImage missingImage = new BufferedImage(24, 24, BufferedImage.TYPE_INT_RGB);
+
+    LegendProcessor.IconTask iconTask = prepareTest(missingImage, statusCode);
+
+    Object[] result = iconTask.call();
+
+    assertEquals(missingImage, result[1]);
+  }
+
+  @Test
+  public void call_iconImageDoesNotExist_returnsMissingImageWithUnsupportedStatus()
+      throws Exception {
+    int statusCode = 999;
+    BufferedImage missingImage = new BufferedImage(24, 24, BufferedImage.TYPE_INT_RGB);
+
+    LegendProcessor.IconTask iconTask = prepareTest(missingImage, statusCode);
+
+    Object[] result = iconTask.call();
+
+    assertEquals(missingImage, result[1]);
+  }
+
+  private static LegendProcessor.IconTask prepareTest(BufferedImage missingImage, int statusCode)
+      throws URISyntaxException, IOException {
+    LegendProcessor legendProcessorMock = spy(LegendProcessor.class);
+    when(legendProcessorMock.getMissingImage()).thenReturn(missingImage);
+    Processor.ExecutionContext context = new AbstractProcessor.Context(new HashMap<>());
+    URL icon = mock(URL.class);
+    when(icon.toURI()).thenReturn(new URI("http://localhost/icon.png"));
+    when(icon.getProtocol()).thenReturn("http");
+    MfClientHttpRequestFactory requestFactoryMock = mock(MfClientHttpRequestFactory.class);
+    MockClientHttpRequest requestMock = new MockClientHttpRequest();
+    when(requestFactoryMock.createRequest(any(URI.class), any())).thenReturn(requestMock);
+
+    byte[] responseBody = "Image bytes".getBytes(Charset.defaultCharset());
+    MockClientHttpResponse responseMock = new MockClientHttpResponse(responseBody, statusCode);
+    responseMock.getHeaders().setContentType(MediaType.IMAGE_PNG);
+    when(requestFactoryMock.createRequest(
+            new URI("testUriString"), org.springframework.http.HttpMethod.GET))
+        .thenReturn(requestMock);
+    requestMock.setResponse(responseMock);
+
+    return legendProcessorMock
+    .new IconTask(icon, 96, context, 1, null, requestFactoryMock, new MetricRegistry());
+  }
+}