This is an automated email from the ASF dual-hosted git repository.

desruisseaux pushed a commit to branch geoapi-4.0
in repository https://gitbox.apache.org/repos/asf/sis.git


The following commit(s) were added to refs/heads/geoapi-4.0 by this push:
     new 0d0587985d Replace `@BeforeAll` annotations by `Lifecycle.PER_CLASS`. 
It often removes the need to dispose the tested resource.
0d0587985d is described below

commit 0d0587985d578a8917e9619f5f33dc1cb29f619c
Author: Martin Desruisseaux <martin.desruisse...@geomatys.com>
AuthorDate: Sun Feb 18 17:44:19 2024 +0100

    Replace `@BeforeAll` annotations by `Lifecycle.PER_CLASS`.
    It often removes the need to dispose the tested resource.
---
 .../sis/util/iso/DefaultNameFactoryTest.java       | 25 +-----
 .../org/apache/sis/util/iso/DefaultRecordTest.java | 40 +++------
 .../apache/sis/util/iso/DefaultRecordTypeTest.java | 30 ++-----
 .../apache/sis/util/iso/NameMarshallingTest.java   | 14 +---
 .../sis/util/iso/SerializableRecordSchema.java     | 20 ++++-
 .../apache/sis/xml/bind/gco/MultiplicityTest.java  | 14 +---
 .../apache/sis/xml/bind/gml/TimePeriodTest.java    | 45 +++-------
 .../apache/sis/xml/bind/lan/LanguageCodeTest.java  | 29 ++-----
 .../sis/openoffice/ReferencingFunctionsTest.java   | 23 +----
 .../org/apache/sis/openoffice/TransformerTest.java | 23 +----
 .../org/apache/sis/io/wkt/ComparisonWithEPSG.java  | 30 +++----
 .../apache/sis/parameter/ParameterFormatTest.java  | 25 ++----
 .../org/apache/sis/referencing/cs/CodesTest.java   |  2 +-
 .../sis/referencing/factory/TestFactorySource.java | 91 ++++++++++----------
 .../referencing/factory/sql/EPSGFactoryTest.java   | 98 ++++++++--------------
 .../operation/CoordinateOperationFinderTest.java   | 88 ++++++++++---------
 .../operation/CoordinateOperationRegistryTest.java | 43 +++++-----
 .../DefaultCoordinateOperationFactoryTest.java     | 35 ++++----
 .../transform/CoordinateSystemTransformTest.java   | 37 ++++----
 .../operation/transform/MathTransformTestCase.java | 31 ++++++-
 .../integration/CoordinateReferenceSystemTest.java |  7 +-
 .../sis/storage/geotiff/SelfConsistencyTest.java   | 49 +++++------
 .../sis/storage/netcdf/SelfConsistencyTest.java    | 50 +++++------
 .../org/apache/sis/storage/gpx/StoreProvider.java  | 13 +++
 .../org/apache/sis/storage/gpx/ReaderTest.java     | 25 +-----
 .../org/apache/sis/storage/gpx/UpdaterTest.java    | 23 +----
 .../org/apache/sis/storage/gpx/WriterTest.java     | 21 +----
 .../sis/storage/esri/BILConsistencyTest.java       | 45 ++++------
 .../sis/storage/esri/BIPConsistencyTest.java       | 45 ++++------
 .../sis/storage/esri/BSQConsistencyTest.java       | 45 ++++------
 .../sis/storage/image/SelfConsistencyTest.java     | 45 ++++------
 .../sis/storage/test/CoverageReadConsistency.java  | 83 ++++++++++++++----
 .../apache/sis/test/FailureDetailsReporter.java    |  6 +-
 .../test/org/apache/sis/test/TestCase.java         |  9 +-
 .../test/org/apache/sis/test/TestUtilities.java    | 15 ++--
 .../storage/coveragejson/binding/BindingTest.java  | 16 ++--
 36 files changed, 513 insertions(+), 727 deletions(-)

diff --git 
a/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/util/iso/DefaultNameFactoryTest.java
 
b/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/util/iso/DefaultNameFactoryTest.java
index 4eba3bb678..7f13bea30c 100644
--- 
a/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/util/iso/DefaultNameFactoryTest.java
+++ 
b/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/util/iso/DefaultNameFactoryTest.java
@@ -20,8 +20,6 @@ import org.opengis.util.GenericName;
 import org.apache.sis.util.SimpleInternationalString;
 
 // Test dependencies
-import org.junit.jupiter.api.BeforeAll;
-import org.junit.jupiter.api.AfterAll;
 import org.junit.jupiter.api.Test;
 import static org.junit.jupiter.api.Assertions.*;
 import org.opengis.test.util.NameTest;
@@ -34,32 +32,11 @@ import org.opengis.test.util.NameTest;
  * @author  Martin Desruisseaux (Geomatys)
  */
 public final class DefaultNameFactoryTest extends NameTest {
-    /**
-     * The factory to test.
-     */
-    private static DefaultNameFactory factorySIS;
-
     /**
      * Creates a new test suite using the singleton factory instance.
      */
     public DefaultNameFactoryTest() {
-        super(factorySIS);
-    }
-
-    /**
-     * Creates the singleton factory instance to be reused for all tests in 
this class.
-     */
-    @BeforeAll
-    public static void createFactory() {
-        factorySIS = new DefaultNameFactory();
-    }
-
-    /**
-     * Disposes the singleton factory instance after all tests have been 
executed.
-     */
-    @AfterAll
-    public static void disposeFactory() {
-        factorySIS = null;
+        super(DefaultNameFactory.provider());
     }
 
     /**
diff --git 
a/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/util/iso/DefaultRecordTest.java
 
b/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/util/iso/DefaultRecordTest.java
index 2ccc80c0d2..575f7c0a39 100644
--- 
a/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/util/iso/DefaultRecordTest.java
+++ 
b/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/util/iso/DefaultRecordTest.java
@@ -23,13 +23,11 @@ import org.opengis.util.RecordType;
 
 // Test dependencies
 import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.AfterAll;
-import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.TestInstance;
 import static org.junit.jupiter.api.Assertions.*;
 import org.apache.sis.test.TestCase;
 import static org.apache.sis.test.Assertions.assertMessageContains;
 import static org.apache.sis.test.Assertions.assertMultilinesEquals;
-import static org.apache.sis.test.Assertions.assertSerializedEquals;
 
 
 /**
@@ -37,40 +35,29 @@ import static 
org.apache.sis.test.Assertions.assertSerializedEquals;
  *
  * @author  Martin Desruisseaux (Geomatys)
  */
+@TestInstance(TestInstance.Lifecycle.PER_CLASS)
 public final class DefaultRecordTest extends TestCase {
+    /**
+     * The record schema for the record types to create.
+     */
+    private final SerializableRecordSchema schema;
+
     /**
      * The record type to be shared by all tests.
      */
-    private static RecordType recordType;
+    private final RecordType recordType;
 
     /**
      * Creates the {@link DefaultRecordType} to be used by all tests in this 
class.
      */
-    @BeforeAll
-    public static void createRecordType() {
-        final var schema = new SerializableRecordSchema("MySchema");
+    public DefaultRecordTest() {
         final var members = new LinkedHashMap<CharSequence,Class<?>>(8);
         assertNull(members.put("city",       String.class));
         assertNull(members.put("latitude",   Double.class));
         assertNull(members.put("longitude",  Double.class));
         assertNull(members.put("population", Integer.class));
+        schema     = new SerializableRecordSchema("MySchema");
         recordType = schema.createRecordType("MyRecordType", members);
-        SerializableRecordSchema.INSTANCE = schema;
-    }
-
-    /**
-     * Clears the {@link DefaultRecordType} used by the tests.
-     */
-    @AfterAll
-    public static void clearRecordType() {
-        SerializableRecordSchema.INSTANCE = null;
-        recordType = null;
-    }
-
-    /**
-     * Creates a new test case.
-     */
-    public DefaultRecordTest() {
     }
 
     /**
@@ -166,7 +153,7 @@ public final class DefaultRecordTest extends TestCase {
     public void testSerialization() {
         final var record = new DefaultRecord(recordType);
         record.setAll("Machu Picchu", -13.1639, -72.5468, null);
-        assertNotSame(record, assertSerializedEquals(record));
+        assertNotSame(record, schema.testSerialization(record));
     }
 
     /**
@@ -178,8 +165,7 @@ public final class DefaultRecordTest extends TestCase {
         final var members = new LinkedHashMap<CharSequence,Class<?>>(8);
         assertNull(members.put("latitude",  Double.class));
         assertNull(members.put("longitude", Double.class));
-        final DefaultRecord record = new DefaultRecord(
-                
SerializableRecordSchema.INSTANCE.createRecordType("MyRecordType", members));
+        final var record = new 
DefaultRecord(schema.createRecordType("MyRecordType", members));
         /*
          * As a side effect of the fact that DefaultRecord uses an array of 
primitive type,
          * initial values should be zero instead of null. We use this trick as 
a way to
@@ -197,6 +183,6 @@ public final class DefaultRecordTest extends TestCase {
                 "    longitude : -72.5468\n" +
                 "}\n",
                 record.toString());
-        assertNotSame(record, assertSerializedEquals(record));
+        assertNotSame(record, schema.testSerialization(record));
     }
 }
diff --git 
a/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/util/iso/DefaultRecordTypeTest.java
 
b/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/util/iso/DefaultRecordTypeTest.java
index 8179e9572d..bd08d2fa56 100644
--- 
a/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/util/iso/DefaultRecordTypeTest.java
+++ 
b/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/util/iso/DefaultRecordTypeTest.java
@@ -27,7 +27,6 @@ import org.junit.jupiter.api.Test;
 import static org.junit.jupiter.api.Assertions.*;
 import org.apache.sis.test.TestCase;
 import static org.apache.sis.test.Assertions.assertMessageContains;
-import static org.apache.sis.test.Assertions.assertSerializedEquals;
 import static org.apache.sis.test.TestUtilities.getSingleton;
 
 
@@ -37,22 +36,17 @@ import static 
org.apache.sis.test.TestUtilities.getSingleton;
  * @author  Martin Desruisseaux (Geomatys)
  */
 public final class DefaultRecordTypeTest extends TestCase {
-    /** Value of {@link DefaultRecordType#getContainer()}.  */ private 
DefaultRecordSchema container;
-    /** Value of {@link DefaultRecordType#getTypeName()}.   */ private 
DefaultTypeName     recordTypeName;
-    /** Value of {@link DefaultRecordType#getMembers()}.    */ private 
DefaultMemberName   fieldName;
-    /** Value of {@link DefaultRecordType#getFieldTypes()}. */ private 
DefaultTypeName     fieldTypeName;
+    /** Value of {@link DefaultRecordType#getContainer()}. */
+    private final SerializableRecordSchema container;
+
+    /** Value of {@link DefaultRecordType#getTypeName()}.   */ private 
DefaultTypeName   recordTypeName;
+    /** Value of {@link DefaultRecordType#getMembers()}.    */ private 
DefaultMemberName fieldName;
+    /** Value of {@link DefaultRecordType#getFieldTypes()}. */ private 
DefaultTypeName   fieldTypeName;
 
     /**
      * Creates a new test case.
      */
     public DefaultRecordTypeTest() {
-    }
-
-    /**
-     * Initializes the private fields.
-     * This method shall be invoked only once per test.
-     */
-    private void init() {
         final DefaultNameSpace recordNamespace;
         final DefaultNameSpace fieldNamespace;
 
@@ -80,7 +74,6 @@ public final class DefaultRecordTypeTest extends TestCase {
     @Test
     @SuppressWarnings("deprecation")
     public void testConstructor() {
-        init();
         final DefaultRecordType type = create();
         assertEquals(1, type.size());
         assertEquals(Integer.TYPE, type.baseValueClass());
@@ -101,7 +94,6 @@ public final class DefaultRecordTypeTest extends TestCase {
      */
     @Test
     public void testArgumentChecks() {
-        init();
         final DefaultTypeName  correctRecordName      = recordTypeName;
         final NameSpace        correctMemberNamespace = fieldName.scope();
         final DefaultNameSpace wrongNamespace         = new 
DefaultNameSpace(null, "WrongNameSpace", ":", ":");
@@ -137,14 +129,6 @@ public final class DefaultRecordTypeTest extends TestCase {
      */
     @Test
     public void testSerialization() {
-        init();
-        synchronized (SerializableRecordSchema.class) {
-            try {
-                SerializableRecordSchema.INSTANCE = container;
-                assertSerializedEquals(create());
-            } finally {
-                SerializableRecordSchema.INSTANCE = null;
-            }
-        }
+        container.testSerialization(create());
     }
 }
diff --git 
a/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/util/iso/NameMarshallingTest.java
 
b/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/util/iso/NameMarshallingTest.java
index a2b4c5a9c4..37bb603ecb 100644
--- 
a/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/util/iso/NameMarshallingTest.java
+++ 
b/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/util/iso/NameMarshallingTest.java
@@ -32,8 +32,8 @@ import org.apache.sis.xml.MarshallerPool;
 import org.apache.sis.xml.util.LegacyNamespaces;
 
 // Test dependencies
-import org.junit.jupiter.api.AfterAll;
 import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestInstance;
 import static org.junit.jupiter.api.Assertions.*;
 import org.apache.sis.test.mock.IdentifiedObjectMock;
 import org.apache.sis.xml.test.TestCase;
@@ -46,13 +46,14 @@ import static 
org.apache.sis.metadata.Assertions.assertXmlEquals;
  * @author  Martin Desruisseaux (Geomatys)
  * @author  Cullen Rombach (Image Matters)
  */
+@TestInstance(TestInstance.Lifecycle.PER_CLASS)
 public final class NameMarshallingTest extends TestCase {
     /**
      * A poll of configured {@link Marshaller} and {@link Unmarshaller}, 
created when first needed.
      *
      * @see #disposeMarshallerPool()
      */
-    private static MarshallerPool pool;
+    private MarshallerPool pool;
 
     /**
      * Creates a new test case.
@@ -200,13 +201,4 @@ public final class NameMarshallingTest extends TestCase {
         assertXmlEquals(expected, actual, "xmlns:*");
         assertEquals(name, unmarshal(expected));
     }
-
-    /**
-     * Invoked by JUnit after the execution of every tests in order to dispose
-     * the {@link MarshallerPool} instance used internally by this class.
-     */
-    @AfterAll
-    public static void disposeMarshallerPool() {
-        pool = null;
-    }
 }
diff --git 
a/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/util/iso/SerializableRecordSchema.java
 
b/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/util/iso/SerializableRecordSchema.java
index 6bb177d141..c02cf2a7b3 100644
--- 
a/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/util/iso/SerializableRecordSchema.java
+++ 
b/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/util/iso/SerializableRecordSchema.java
@@ -18,6 +18,7 @@ package org.apache.sis.util.iso;
 
 import java.io.Serializable;
 import java.io.ObjectStreamException;
+import static org.apache.sis.test.Assertions.assertSerializedEquals;
 
 
 /**
@@ -31,7 +32,7 @@ final class SerializableRecordSchema extends 
DefaultRecordSchema implements Seri
     /**
      * The unique instance for the shema.
      */
-    static DefaultRecordSchema INSTANCE;
+    private static DefaultRecordSchema INSTANCE;
 
     /**
      * Construct a new record schema.
@@ -58,4 +59,21 @@ final class SerializableRecordSchema extends 
DefaultRecordSchema implements Seri
             return INSTANCE;
         }
     }
+
+    /**
+     * Tests serialization of a {@code Record} or {@code RecordType}.
+     *
+     * @param  record the object to serialize.
+     * @return the deserialized object.
+     */
+    final Object testSerialization(final Object record) {
+        synchronized (SerializableRecordSchema.class) {
+            try {
+                INSTANCE = this;
+                return assertSerializedEquals(record);
+            } finally {
+                INSTANCE = null;
+            }
+        }
+    }
 }
diff --git 
a/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/xml/bind/gco/MultiplicityTest.java
 
b/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/xml/bind/gco/MultiplicityTest.java
index b287098065..0c6e89083a 100644
--- 
a/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/xml/bind/gco/MultiplicityTest.java
+++ 
b/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/xml/bind/gco/MultiplicityTest.java
@@ -27,8 +27,8 @@ import org.apache.sis.xml.XML;
 import org.apache.sis.metadata.xml.TestUsingFile;
 
 // Test dependencies
-import org.junit.jupiter.api.AfterAll;
 import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestInstance;
 import static org.junit.jupiter.api.Assertions.*;
 import org.apache.sis.test.mock.FeatureAttributeMock;
 
@@ -38,6 +38,7 @@ import org.apache.sis.test.mock.FeatureAttributeMock;
  *
  * @author  Martin Desruisseaux (Geomatys)
  */
+@TestInstance(TestInstance.Lifecycle.PER_CLASS)
 public final class MultiplicityTest extends TestUsingFile {
     /**
      * Opens the stream to the XML file containing multiplicity declarations.
@@ -52,7 +53,7 @@ public final class MultiplicityTest extends TestUsingFile {
     /**
      * A poll of configured {@code Marshaller} and {@code Unmarshaller}.
      */
-    private static MarshallerPool pool;
+    private MarshallerPool pool;
 
     /**
      * Creates a new test case.
@@ -77,15 +78,6 @@ public final class MultiplicityTest extends TestUsingFile {
         return pool;
     }
 
-    /**
-     * Invoked by JUnit after the execution of every tests in order to dispose
-     * the {@link MarshallerPool} instance used internally by this class.
-     */
-    @AfterAll
-    public static void disposeMarshallerPool() {
-        pool = null;
-    }
-
     /**
      * Creates a multiplicity instance with the same content as in {@value 
#FILENAME} file.
      *
diff --git 
a/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/xml/bind/gml/TimePeriodTest.java
 
b/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/xml/bind/gml/TimePeriodTest.java
index 9627100492..7f0b452cd3 100644
--- 
a/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/xml/bind/gml/TimePeriodTest.java
+++ 
b/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/xml/bind/gml/TimePeriodTest.java
@@ -16,7 +16,6 @@
  */
 package org.apache.sis.xml.bind.gml;
 
-import java.util.Map;
 import java.util.HashMap;
 import java.util.Locale;
 import javax.xml.datatype.DatatypeConfigurationException;
@@ -31,9 +30,8 @@ import org.apache.sis.xml.util.XmlUtilities;
 import org.apache.sis.pending.temporal.DefaultTemporalFactory;
 
 // Test dependencies
-import org.junit.jupiter.api.BeforeAll;
-import org.junit.jupiter.api.AfterAll;
 import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestInstance;
 import static org.junit.jupiter.api.Assertions.*;
 import org.apache.sis.xml.test.TestCase;
 import static org.apache.sis.metadata.Assertions.assertXmlEquals;
@@ -50,16 +48,24 @@ import org.opengis.temporal.Instant;
  *
  * @author  Martin Desruisseaux (Geomatys)
  */
+@TestInstance(TestInstance.Lifecycle.PER_CLASS)
 public final class TimePeriodTest extends TestCase {
     /**
-     * A poll of configured {@link Marshaller} and {@link Unmarshaller}, 
created when first needed.
+     * A poll of configured {@link Marshaller} and {@link Unmarshaller}.
      */
-    private static MarshallerPool pool;
+    private final MarshallerPool pool;
 
     /**
-     * Creates a new test case.
+     * Creates the XML (un)marshaller pool to be shared by all test methods.
+     * The (un)marshallers locale and timezone will be set to fixed values.
+     *
+     * @throws JAXBException if an error occurred while creating the pool.
      */
-    public TimePeriodTest() {
+    public TimePeriodTest() throws JAXBException {
+        final var properties = new HashMap<String,Object>(4);
+        assertNull(properties.put(XML.LOCALE, Locale.FRANCE));
+        assertNull(properties.put(XML.TIMEZONE, "CET"));
+        pool = new MarshallerPool(JAXBContext.newInstance(TimeInstant.class, 
TimePeriod.class), properties);
     }
 
     /**
@@ -77,31 +83,6 @@ public final class TimePeriodTest extends TestCase {
         return DefaultTemporalFactory.provider().createInstant(date(date));
     }
 
-    /**
-     * Creates the XML (un)marshaller pool to be shared by all test methods.
-     * The (un)marshallers locale and timezone will be set to fixed values.
-     *
-     * @throws JAXBException if an error occurred while creating the pool.
-     *
-     * @see #disposeMarshallerPool()
-     */
-    @BeforeAll
-    public static void createMarshallerPool() throws JAXBException {
-        final Map<String,Object> properties = new HashMap<>(4);
-        assertNull(properties.put(XML.LOCALE, Locale.FRANCE));
-        assertNull(properties.put(XML.TIMEZONE, "CET"));
-        pool = new MarshallerPool(JAXBContext.newInstance(TimeInstant.class, 
TimePeriod.class), properties);
-    }
-
-    /**
-     * Invoked by JUnit after the execution of every tests in order to dispose
-     * the {@link MarshallerPool} instance used internally by this class.
-     */
-    @AfterAll
-    public static void disposeMarshallerPool() {
-        pool = null;
-    }
-
     /**
      * Tests time instant. The test is executed using an arbitrary locale and 
timezone.
      *
diff --git 
a/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/xml/bind/lan/LanguageCodeTest.java
 
b/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/xml/bind/lan/LanguageCodeTest.java
index 6af6a90be4..629ddff733 100644
--- 
a/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/xml/bind/lan/LanguageCodeTest.java
+++ 
b/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/xml/bind/lan/LanguageCodeTest.java
@@ -16,7 +16,6 @@
  */
 package org.apache.sis.xml.bind.lan;
 
-import java.util.Map;
 import java.util.HashMap;
 import java.util.Locale;
 import jakarta.xml.bind.Marshaller;
@@ -31,9 +30,8 @@ import org.apache.sis.xml.util.LegacyNamespaces;
 import static org.apache.sis.util.internal.StandardDateFormat.UTC;
 
 // Test dependencies
-import org.junit.jupiter.api.BeforeAll;
-import org.junit.jupiter.api.AfterAll;
 import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestInstance;
 import static org.junit.jupiter.api.Assertions.*;
 import org.apache.sis.test.mock.MetadataMock;
 import org.apache.sis.xml.test.TestCase;
@@ -53,6 +51,7 @@ import static org.apache.sis.test.TestUtilities.getSingleton;
  *
  * @author  Martin Desruisseaux (Geomatys)
  */
+@TestInstance(TestInstance.Lifecycle.PER_CLASS)
 public final class LanguageCodeTest extends TestCase {
     /**
      * XML fragment using the {@code <gco:CharacterString>} construct.
@@ -74,13 +73,7 @@ public final class LanguageCodeTest extends TestCase {
     /**
      * A poll of configured {@link Marshaller} and {@link Unmarshaller}, 
created when first needed.
      */
-    private static MarshallerPool pool;
-
-    /**
-     * Creates a new test case.
-     */
-    public LanguageCodeTest() {
-    }
+    private final MarshallerPool pool;
 
     /**
      * Creates the XML (un)marshaller pool to be shared by all test methods.
@@ -90,27 +83,15 @@ public final class LanguageCodeTest extends TestCase {
      * {@link MetadataMock} instead of {@link 
org.apache.sis.metadata.iso.DefaultMetadata}.</p>
      *
      * @throws JAXBException if an error occurred while creating the pool.
-     *
-     * @see #disposeMarshallerPool()
      */
-    @BeforeAll
-    public static void createMarshallerPool() throws JAXBException {
-        final Map<String,Object> properties = new HashMap<>(4);
+    public LanguageCodeTest() throws JAXBException {
+        final var properties = new HashMap<String,Object>(4);
         assertNull(properties.put(XML.LOCALE, Locale.UK));
         assertNull(properties.put(XML.TIMEZONE, UTC));
         assertNull(properties.put(XML.LENIENT_UNMARSHAL, Boolean.TRUE));
         pool = new MarshallerPool(JAXBContext.newInstance(MetadataMock.class), 
properties);
     }
 
-    /**
-     * Invoked by JUnit after the execution of every tests in order to dispose
-     * the {@link MarshallerPool} instance used internally by this class.
-     */
-    @AfterAll
-    public static void disposeMarshallerPool() {
-        pool = null;
-    }
-
     /**
      * Returns the XML of a metadata element. This method returns a string 
like below,
      * where the {@code ${languageCode}} string is replaced by the given 
argument.
diff --git 
a/endorsed/src/org.apache.sis.openoffice/test/org/apache/sis/openoffice/ReferencingFunctionsTest.java
 
b/endorsed/src/org.apache.sis.openoffice/test/org/apache/sis/openoffice/ReferencingFunctionsTest.java
index 04b5816b29..176517f3bb 100644
--- 
a/endorsed/src/org.apache.sis.openoffice/test/org/apache/sis/openoffice/ReferencingFunctionsTest.java
+++ 
b/endorsed/src/org.apache.sis.openoffice/test/org/apache/sis/openoffice/ReferencingFunctionsTest.java
@@ -21,9 +21,8 @@ import org.apache.sis.referencing.util.Formulas;
 import org.apache.sis.referencing.util.PositionalAccuracyConstant;
 
 // Test dependencies
-import org.junit.jupiter.api.BeforeAll;
-import org.junit.jupiter.api.AfterAll;
 import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestInstance;
 import static org.junit.jupiter.api.Assertions.*;
 import static org.junit.jupiter.api.Assumptions.assumeFalse;
 import org.apache.sis.test.TestCase;
@@ -34,35 +33,21 @@ import org.apache.sis.test.TestCase;
  *
  * @author  Martin Desruisseaux (IRD, Geomatys)
  */
+@TestInstance(TestInstance.Lifecycle.PER_CLASS)
 public final class ReferencingFunctionsTest extends TestCase {
     /**
      * The instance to use for testing purpose.
      */
-    private static ReferencingFunctions instance;
+    private final ReferencingFunctions instance;
 
     /**
      * Creates a {@link ReferencingFunctions} instance to use for all tests.
      */
-    @BeforeAll
-    public static void createReferencingInstance() {
+    public ReferencingFunctionsTest() {
         instance = new ReferencingFunctions(null);
         instance.setLocale(new com.sun.star.lang.Locale("en", "US", null));
     }
 
-    /**
-     * Disposes the {@link ReferencingFunctions} instance after all tests 
completed.
-     */
-    @AfterAll
-    public static void disposeReferencingInstance() {
-        instance = null;
-    }
-
-    /**
-     * Creates a new test case.
-     */
-    public ReferencingFunctionsTest() {
-    }
-
     /**
      * Verifies the service and implementation names.
      */
diff --git 
a/endorsed/src/org.apache.sis.openoffice/test/org/apache/sis/openoffice/TransformerTest.java
 
b/endorsed/src/org.apache.sis.openoffice/test/org/apache/sis/openoffice/TransformerTest.java
index 9b6235e776..2d3951b3c6 100644
--- 
a/endorsed/src/org.apache.sis.openoffice/test/org/apache/sis/openoffice/TransformerTest.java
+++ 
b/endorsed/src/org.apache.sis.openoffice/test/org/apache/sis/openoffice/TransformerTest.java
@@ -22,9 +22,8 @@ import org.apache.sis.referencing.CommonCRS;
 import org.apache.sis.storage.DataStoreException;
 
 // Test dependencies
-import org.junit.jupiter.api.BeforeAll;
-import org.junit.jupiter.api.AfterAll;
 import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestInstance;
 import static org.junit.jupiter.api.Assertions.*;
 import static org.junit.jupiter.api.Assumptions.assumeTrue;
 import org.apache.sis.test.TestCase;
@@ -35,35 +34,21 @@ import org.apache.sis.test.TestCase;
  *
  * @author  Martin Desruisseaux (Geomatys)
  */
+@TestInstance(TestInstance.Lifecycle.PER_CLASS)
 public final class TransformerTest extends TestCase {
     /**
      * The instance to use for testing purpose.
      */
-    private static ReferencingFunctions caller;
+    private final ReferencingFunctions caller;
 
     /**
      * Creates a {@link ReferencingFunctions} instance to use for all tests.
      */
-    @BeforeAll
-    public static void createReferencingInstance() {
+    public TransformerTest() {
         caller = new ReferencingFunctions(null);
         caller.setLocale(new com.sun.star.lang.Locale("en", "US", null));
     }
 
-    /**
-     * Disposes the {@link ReferencingFunctions} instance after all tests 
completed.
-     */
-    @AfterAll
-    public static void disposeReferencingInstance() {
-        caller = null;
-    }
-
-    /**
-     * Creates a new test case.
-     */
-    public TransformerTest() {
-    }
-
     /**
      * Asserts that the transformation result is equal to the expected result.
      */
diff --git 
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/io/wkt/ComparisonWithEPSG.java
 
b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/io/wkt/ComparisonWithEPSG.java
index e9edebf9ea..1a867e7108 100644
--- 
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/io/wkt/ComparisonWithEPSG.java
+++ 
b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/io/wkt/ComparisonWithEPSG.java
@@ -25,14 +25,11 @@ import org.apache.sis.referencing.factory.TestFactorySource;
 import org.apache.sis.referencing.factory.sql.EPSGFactory;
 
 // Test dependencies
-import org.junit.jupiter.api.BeforeAll;
 import org.junit.jupiter.api.AfterAll;
 import org.junit.jupiter.api.Test;
-import static org.junit.jupiter.api.Assumptions.assumeTrue;
-import org.apache.sis.test.TestCase;
+import org.junit.jupiter.api.TestInstance;
 import static org.apache.sis.test.Assertions.assertEqualsIgnoreMetadata;
-
-// Specific to the geoapi-3.1 and geoapi-4.0 branches:
+import org.apache.sis.test.TestCase;
 
 
 /**
@@ -40,21 +37,20 @@ import static 
org.apache.sis.test.Assertions.assertEqualsIgnoreMetadata;
  *
  * @author  Martin Desruisseaux (Geomatys)
  */
+@TestInstance(TestInstance.Lifecycle.PER_CLASS)
 public final class ComparisonWithEPSG extends TestCase {
     /**
-     * Creates a new test case.
+     * The source of the EPSG factory.
      */
-    public ComparisonWithEPSG() {
-    }
+    private final TestFactorySource dataEPSG;
 
     /**
      * Creates the factory to use for all tests in this class.
      *
      * @throws FactoryException if an error occurred while creating the 
factory.
      */
-    @BeforeAll
-    public static void createFactory() throws FactoryException {
-        TestFactorySource.createFactory();
+    public ComparisonWithEPSG() throws FactoryException {
+        dataEPSG = new TestFactorySource();
     }
 
     /**
@@ -63,8 +59,8 @@ public final class ComparisonWithEPSG extends TestCase {
      * @throws FactoryException if an error occurred while closing the 
connections.
      */
     @AfterAll
-    public static void close() throws FactoryException {
-        TestFactorySource.close();
+    public void close() throws FactoryException {
+        dataEPSG.close();
     }
 
     /**
@@ -157,10 +153,9 @@ public final class ComparisonWithEPSG extends TestCase {
      * Compares a projected CRS parsed from a WKT with a the CRS built from 
EPSG database.
      * The latter is taken as the reference.
      */
-    private static void compare(final String wkt, final int epsg) throws 
FactoryException {
+    private void compare(final String wkt, final int epsg) throws 
FactoryException {
         final CoordinateReferenceSystem crs = CRS.fromWKT(wkt);
-        final EPSGFactory factory = TestFactorySource.factory;
-        assumeTrue(factory != null);
+        final EPSGFactory factory = dataEPSG.factory();
         final CoordinateReferenceSystem reference = 
factory.createProjectedCRS(Integer.toString(epsg));
         assertEqualsIgnoreMetadata(reference, crs);
     }
@@ -175,8 +170,7 @@ public final class ComparisonWithEPSG extends TestCase {
      */
     @Test
     public void testCoordinateOperation() throws FactoryException, 
ParseException {
-        final EPSGFactory factory = TestFactorySource.factory;
-        assumeTrue(factory != null);
+        final EPSGFactory factory = dataEPSG.factory();
         CoordinateOperation opFromCode = 
factory.createCoordinateOperation("5630");
         String wkt = opFromCode.toWKT();
         WKTFormat parser = new WKTFormat();
diff --git 
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/parameter/ParameterFormatTest.java
 
b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/parameter/ParameterFormatTest.java
index cd6171d5e8..6715660dc8 100644
--- 
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/parameter/ParameterFormatTest.java
+++ 
b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/parameter/ParameterFormatTest.java
@@ -29,9 +29,8 @@ import static 
org.apache.sis.metadata.iso.citation.Citations.OGC;
 import static org.apache.sis.metadata.iso.citation.Citations.EPSG;
 
 // Test dependencies
-import org.junit.jupiter.api.BeforeAll;
-import org.junit.jupiter.api.AfterAll;
 import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestInstance;
 import static org.junit.jupiter.api.Assertions.*;
 import org.apache.sis.test.TestCase;
 import static org.apache.sis.test.Assertions.assertMultilinesEquals;
@@ -42,17 +41,12 @@ import static 
org.apache.sis.test.Assertions.assertMultilinesEquals;
  *
  * @author  Martin Desruisseaux (Geomatys)
  */
+@TestInstance(TestInstance.Lifecycle.PER_CLASS)
 public final class ParameterFormatTest extends TestCase {
     /**
      * The parameter descriptors used for all tests in this class.
      */
-    private static ParameterDescriptorGroup descriptor;
-
-    /**
-     * Creates a new test case.
-     */
-    public ParameterFormatTest() {
-    }
+    private final ParameterDescriptorGroup descriptor;
 
     /**
      * Creates the parameter descriptors to be used by all tests in this 
class. This method creates
@@ -60,8 +54,7 @@ public final class ParameterFormatTest extends TestCase {
      * with arbitrary non-zero default values. Those default values are not 
part of EPSG definitions.
      * They are added here only for testing purpose.
      */
-    @BeforeAll
-    public static void createParameterDescriptor() {
+    public ParameterFormatTest() {
         descriptor = createMercatorParameters();
     }
 
@@ -88,21 +81,13 @@ public final class ParameterFormatTest extends TestCase {
         return builder.createGroup(parameters);
     }
 
-    /**
-     * Forgets the parameter descriptors after all tests are done.
-     */
-    @AfterAll
-    public static void clearParameterDescriptor() {
-        descriptor = null;
-    }
-
     /**
      * Creates parameter values with some arbitrary values different than the 
default values.
      * This method intentionally leaves {@code "central_meridian"} (a 
mandatory parameter) and
      * {@code "false_easting"} (an optional parameter) undefined, in order to 
test whether the
      * formatter fallback on default values.
      */
-    private static ParameterValueGroup createParameterValues() {
+    private ParameterValueGroup createParameterValues() {
         final ParameterValueGroup group = descriptor.createValue();
         group.parameter("latitude_of_origin").setValue(20);
         group.parameter("scale_factor").setValue(0.997);
diff --git 
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/cs/CodesTest.java
 
b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/cs/CodesTest.java
index 72d54a3556..154cd4e3a2 100644
--- 
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/cs/CodesTest.java
+++ 
b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/cs/CodesTest.java
@@ -23,12 +23,12 @@ import org.opengis.referencing.cs.AxisDirection;
 import org.opengis.referencing.cs.CoordinateSystem;
 import org.opengis.referencing.cs.CSAuthorityFactory;
 import org.apache.sis.measure.Units;
-import org.apache.sis.referencing.factory.TestFactorySource;
 
 // Test dependencies
 import org.junit.jupiter.api.Test;
 import static org.junit.jupiter.api.Assertions.*;
 import org.apache.sis.test.TestCase;
+import org.apache.sis.referencing.factory.TestFactorySource;
 
 
 /**
diff --git 
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/factory/TestFactorySource.java
 
b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/factory/TestFactorySource.java
index 52620a2019..c5769d1176 100644
--- 
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/factory/TestFactorySource.java
+++ 
b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/factory/TestFactorySource.java
@@ -16,7 +16,6 @@
  */
 package org.apache.sis.referencing.factory;
 
-import java.util.Map;
 import java.util.HashMap;
 import org.postgresql.ds.PGSimpleDataSource;
 import org.opengis.util.FactoryException;
@@ -38,22 +37,26 @@ import static org.opengis.test.Assertions.assertBetween;
  * Use this class as below:
  *
  * {@snippet lang="java" :
- *     @BeforeAll
- *     public static void createFactory() throws FactoryException {
- *         TestFactorySource.createFactory();
- *     }
+ *     @TestInstance(TestInstance.Lifecycle.PER_CLASS)
+ *     public class MyTest {
+ *         private final TestFactorySource dataEPSG;
  *
- *     @AfterAll
- *     public static void close() throws FactoryException {
- *         TestFactorySource.close();
- *     }
+ *         public MyTest() throws FactoryException {
+ *             dataEPSG = new TestFactorySource();
+ *         }
  *
- *     @Test
- *     public void testFoo() {
- *         assumeNotNull(TestFactorySource.factory);
- *         // Test can happen now.
+ *         @AfterAll
+ *         public void close() throws FactoryException {
+ *             dataEPSG.close();
+ *         }
+ *
+ *         @Test
+ *         public void testFoo() {
+ *             final EPSGFactory factory = dataEPSG.factory();
+ *             // Test can happen now.
+ *         }
+ *     }
  *     }
- * }
  *
  * @author  Martin Desruisseaux (Geomatys)
  */
@@ -79,30 +82,20 @@ public final class TestFactorySource {
 
     /**
      * The factory instance to use for the tests, or {@code null} if not 
available.
-     * This field is set by {@link #createFactory()} and cleared by {@link 
#close()}.
-     * Test classes using this field shall declare their own {@code 
createFactory()}
-     * and {@code close()} methods delegating their work to the corresponding 
methods
-     * in this {@code TestFactorySource} class.
      */
-    public static EPSGFactory factory;
+    private final EPSGFactory factory;
 
     /**
      * {@code true} if we failed to create the {@link #factory}.
      */
     private static boolean isUnavailable;
 
-    /**
-     * Do not allow instantiation of this class.
-     */
-    private TestFactorySource() {
-    }
-
     /**
      * Returns the system-wide EPSG factory, or interrupts the tests with 
JUnit {@code Assumptions}
      * if the EPSG factory is not available. Note that this method breaks 
isolation between tests.
      * For more isolated tests, use {@link #createFactory()} and {@link 
#close()} instead.
      *
-     * @return the system-wide EPSG factory.
+     * @return the system-wide EPSG factory. Never null if this method returns.
      * @throws FactoryException if an error occurred while fetching the 
factory.
      */
     public static synchronized EPSGFactory getSharedFactory() throws 
FactoryException {
@@ -125,49 +118,61 @@ public final class TestFactorySource {
      *
      * @throws FactoryException if an error occurred while creating the 
factory.
      */
-    public static synchronized void createFactory() throws FactoryException {
-        if (!isUnavailable) {
-            EPSGFactory af = factory;
-            if (af == null) {
+    public TestFactorySource() throws FactoryException {
+        @SuppressWarnings("LocalVariableHidesMemberVariable")
+        EPSGFactory factory = null;
+        synchronized (TestFactorySource.class) {
+            if (!isUnavailable) {
                 final GeodeticObjectFactory f = 
GeodeticObjectFactory.provider();
-                final Map<String,Object> properties = new HashMap<>(6);
+                final var properties = new HashMap<String,Object>(6);
                 assertNull(properties.put("datumFactory", f));
                 assertNull(properties.put("csFactory", f));
                 assertNull(properties.put("crsFactory", f));
+                boolean success = false;
                 try {
-                    af = new EPSGFactory(properties);
-                    assertEquals(0, ((ConcurrentAuthorityFactory) 
af).countAvailableDataAccess(),
+                    factory = new EPSGFactory(properties);
+                    assertEquals(0, ((ConcurrentAuthorityFactory) 
factory).countAvailableDataAccess(),
                                  "Expected no Data Access Object (DAO) before 
the first test is run.");
                     /*
                      * Above method call may fail if no data source has been 
specified.
                      * Following method call may fail if a data source has 
been specified,
                      * but the database does not contain the required tables.
                      */
-                    
assertNotNull(af.createUnit(String.valueOf(Constants.EPSG_METRE)));
-                    factory = af;                                              
             // Must be last.
+                    
assertNotNull(factory.createUnit(String.valueOf(Constants.EPSG_METRE)));
+                    success = true;
                 } catch (UnavailableFactoryException e) {
                     isUnavailable = true;
                     GeodeticAuthorityFactory.LOGGER.warning(e.toString());
                 } finally {
-                    if (factory != af) {
-                        af.close();
+                    if (!success && factory != null) {
+                        factory.close();
+                        factory = null;
                     }
                 }
             }
+            this.factory = factory;
         }
     }
 
+    /**
+     * Returns the factory, or interrupt the test with a JUnit {@code 
Assumptions} if there is no factory available.
+     *
+     * @return the factory (never null if this method returns).
+     */
+    public EPSGFactory factory() {
+        assumeTrue(factory != null, "No connection to EPSG dataset.");
+        return factory;
+    }
+
     /**
      * Forces release of JDBC connections after the tests in a class.
      *
      * @throws FactoryException if an error occurred while closing the 
connections.
      */
-    public static synchronized void close() throws FactoryException {
-        final EPSGFactory af = factory;
-        if (af != null) {
-            factory = null;
-            final int n = ((ConcurrentAuthorityFactory) 
af).countAvailableDataAccess();
-            af.close();
+    public void close() throws FactoryException {
+        if (factory != null) {
+            final int n = ((ConcurrentAuthorityFactory) 
factory).countAvailableDataAccess();
+            factory.close();
             assertBetween(0, 1, n, "Since we ran all tests sequentially, 
should have no more than 1 Data Access Object (DAO).");
         }
     }
diff --git 
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/factory/sql/EPSGFactoryTest.java
 
b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/factory/sql/EPSGFactoryTest.java
index 0445838b85..e92de380bc 100644
--- 
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/factory/sql/EPSGFactoryTest.java
+++ 
b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/factory/sql/EPSGFactoryTest.java
@@ -54,19 +54,18 @@ import org.apache.sis.referencing.datum.BursaWolfParameters;
 import org.apache.sis.referencing.datum.DefaultGeodeticDatum;
 import org.apache.sis.referencing.operation.AbstractCoordinateOperation;
 import org.apache.sis.referencing.factory.IdentifiedObjectFinder;
-import org.apache.sis.referencing.factory.TestFactorySource;
 
 // Test dependencies
 import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestInstance;
 import org.junit.jupiter.api.Disabled;
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.AfterAll;
-import org.junit.jupiter.api.BeforeAll;
 import static org.junit.jupiter.api.Assertions.*;
-import static org.junit.jupiter.api.Assumptions.assumeTrue;
 import org.junit.jupiter.api.extension.RegisterExtension;
 import org.apache.sis.test.TestCase;
 import org.apache.sis.test.LoggingWatcher;
+import org.apache.sis.referencing.factory.TestFactorySource;
 import static org.apache.sis.test.Assertions.assertNotDeepEquals;
 import static 
org.apache.sis.referencing.Assertions.assertEpsgNameAndIdentifierEqual;
 import static org.apache.sis.referencing.Assertions.assertAliasTipEquals;
@@ -81,21 +80,20 @@ import static 
org.opengis.test.Assertions.assertAxisDirectionsEqual;
  * @author  Martin Desruisseaux (IRD, Geomatys)
  * @author  Vadim Semenov
  */
+@TestInstance(TestInstance.Lifecycle.PER_CLASS)
 public final class EPSGFactoryTest extends TestCase {
     /**
-     * Creates a new test case.
+     * The source of the EPSG factory.
      */
-    public EPSGFactoryTest() {
-    }
+    private final TestFactorySource dataEPSG;
 
     /**
      * Creates the factory to use for all tests in this class.
      *
      * @throws FactoryException if an error occurred while creating the 
factory.
      */
-    @BeforeAll
-    public static void createFactory() throws FactoryException {
-        TestFactorySource.createFactory();
+    public EPSGFactoryTest() throws FactoryException {
+        dataEPSG = new TestFactorySource();
     }
 
     /**
@@ -104,8 +102,8 @@ public final class EPSGFactoryTest extends TestCase {
      * @throws FactoryException if an error occurred while closing the 
connections.
      */
     @AfterAll
-    public static void close() throws FactoryException {
-        TestFactorySource.close();
+    public void close() throws FactoryException {
+        dataEPSG.close();
     }
 
     /**
@@ -130,8 +128,7 @@ public final class EPSGFactoryTest extends TestCase {
      */
     @Test
     public void testWGS84() throws FactoryException {
-        final EPSGFactory factory = TestFactorySource.factory;
-        assumeTrue(factory != null);
+        final EPSGFactory factory = dataEPSG.factory();
         final GeographicCRS crs = factory.createGeographicCRS("EPSG:4326");
         assertEpsgNameAndIdentifierEqual("WGS 84", 4326, crs);
         assertEpsgNameAndIdentifierEqual("World Geodetic System 1984", 6326, 
crs.getDatum());
@@ -151,8 +148,7 @@ public final class EPSGFactoryTest extends TestCase {
      */
     @Test
     public void testGeographic2D() throws FactoryException {
-        final EPSGFactory factory = TestFactorySource.factory;
-        assumeTrue(factory != null);
+        final EPSGFactory factory = dataEPSG.factory();
         final GeographicCRS crs = factory.createGeographicCRS("4274");
         assertEpsgNameAndIdentifierEqual("Datum 73", 4274, crs);
         assertEpsgNameAndIdentifierEqual("Datum 73", 6274, crs.getDatum());
@@ -171,8 +167,7 @@ public final class EPSGFactoryTest extends TestCase {
      */
     @Test
     public void testGeographic3D() throws FactoryException {
-        final EPSGFactory factory = TestFactorySource.factory;
-        assumeTrue(factory != null);
+        final EPSGFactory factory = dataEPSG.factory();
         final GeographicCRS crs = factory.createGeographicCRS("EPSG::4993");
         assertEpsgNameAndIdentifierEqual("Lao 1997", 4993, crs);
         assertEpsgNameAndIdentifierEqual("Lao National Datum 1997", 6678, 
crs.getDatum());
@@ -188,8 +183,7 @@ public final class EPSGFactoryTest extends TestCase {
      */
     @Test
     public void testGeocentric() throws FactoryException {
-        final EPSGFactory factory = TestFactorySource.factory;
-        assumeTrue(factory != null);
+        final EPSGFactory factory = dataEPSG.factory();
         final GeocentricCRS crs = factory.createGeocentricCRS("epsg:4915");
         assertEpsgNameAndIdentifierEqual("ITRF93", 4915, crs);
         assertEpsgNameAndIdentifierEqual("International Terrestrial Reference 
Frame 1993", 6652, crs.getDatum());
@@ -205,8 +199,7 @@ public final class EPSGFactoryTest extends TestCase {
      */
     @Test
     public void testProjected() throws FactoryException {
-        final EPSGFactory factory = TestFactorySource.factory;
-        assumeTrue(factory != null);
+        final EPSGFactory factory = dataEPSG.factory();
         final ProjectedCRS crs = factory.createProjectedCRS("2027");
         assertEpsgNameAndIdentifierEqual("NAD27(76) / UTM zone 15N", 2027, 
crs);
         assertEpsgNameAndIdentifierEqual("NAD27(76)", 4608, crs.getBaseCRS());
@@ -242,8 +235,7 @@ public final class EPSGFactoryTest extends TestCase {
      */
     @Test
     public void testProjectedNorthEast() throws FactoryException {
-        final EPSGFactory factory = TestFactorySource.factory;
-        assumeTrue(factory != null);
+        final EPSGFactory factory = dataEPSG.factory();
         final ProjectedCRS crs = factory.createProjectedCRS(" EPSG : 2442 ");
         assertEpsgNameAndIdentifierEqual("Beijing 1954 / 3-degree Gauss-Kruger 
CM 135E", 2442, crs);
         assertAliasTipEquals            ("Beijing 1954 / 3GK 135E", crs);
@@ -274,8 +266,7 @@ public final class EPSGFactoryTest extends TestCase {
      */
     @Test
     public void testProjectedWithSharedConversion() throws FactoryException {
-        final EPSGFactory factory = TestFactorySource.factory;
-        assumeTrue(factory != null);
+        final EPSGFactory factory = dataEPSG.factory();
         final ProjectedCRS crs = factory.createProjectedCRS("32210");
         assertEpsgNameAndIdentifierEqual("WGS 72 / UTM zone 10N", 32210, crs);
         assertEpsgNameAndIdentifierEqual("WGS 72", 4322, crs.getBaseCRS());
@@ -310,8 +301,7 @@ public final class EPSGFactoryTest extends TestCase {
      */
     @Test
     public void testProjectedByName() throws FactoryException {
-        final EPSGFactory factory = TestFactorySource.factory;
-        assumeTrue(factory != null);
+        final EPSGFactory factory = dataEPSG.factory();
         final ProjectedCRS crs = factory.createProjectedCRS("NTF (Paris) / 
Lambert zone I");
         assertEpsgNameAndIdentifierEqual("NTF (Paris) / Lambert zone I", 
27571, crs);
         assertEpsgNameAndIdentifierEqual("NTF (Paris)", 4807, 
crs.getBaseCRS());
@@ -338,8 +328,7 @@ public final class EPSGFactoryTest extends TestCase {
     @Test
     @Disabled("“Lambert Azimuthal Equal Area (Spherical)” projection is not 
yet implemented.")
     public void testProjectedOnPole() throws FactoryException {
-        final EPSGFactory factory = TestFactorySource.factory;
-        assumeTrue(factory != null);
+        final EPSGFactory factory = dataEPSG.factory();
         final ProjectedCRS crs = factory.createProjectedCRS("3408");
         assertEpsgNameAndIdentifierEqual("NSIDC EASE-Grid North", 3408, crs);
         assertEpsgNameAndIdentifierEqual("Unspecified datum based upon the 
International 1924 Authalic Sphere", 4053, crs.getBaseCRS());
@@ -358,8 +347,7 @@ public final class EPSGFactoryTest extends TestCase {
      */
     @Test
     public void testGoogleProjection() throws FactoryException {
-        final EPSGFactory factory = TestFactorySource.factory;
-        assumeTrue(factory != null);
+        final EPSGFactory factory = dataEPSG.factory();
         final ProjectedCRS crs = factory.createProjectedCRS("3857");
         assertEpsgNameAndIdentifierEqual("WGS 84 / Pseudo-Mercator", 3857, 
crs);
         assertEpsgNameAndIdentifierEqual("WGS 84", 4326, crs.getBaseCRS());
@@ -377,8 +365,7 @@ public final class EPSGFactoryTest extends TestCase {
      */
     @Test
     public void testEngineering() throws FactoryException {
-        final EPSGFactory factory = TestFactorySource.factory;
-        assumeTrue(factory != null);
+        final EPSGFactory factory = dataEPSG.factory();
         final EngineeringCRS crs = factory.createEngineeringCRS("EPSG:5801");
         assertEpsgNameAndIdentifierEqual("Barcelona Grid B1", 5801, crs);
         assertEpsgNameAndIdentifierEqual("Barcelona", 9301, crs.getDatum());
@@ -393,8 +380,7 @@ public final class EPSGFactoryTest extends TestCase {
      */
     @Test
     public void testVertical() throws FactoryException {
-        final EPSGFactory factory = TestFactorySource.factory;
-        assumeTrue(factory != null);
+        final EPSGFactory factory = dataEPSG.factory();
         final VerticalCRS crs = factory.createVerticalCRS("EPSG:5735");
         assertEpsgNameAndIdentifierEqual("Black Sea height", 5735, crs);
         assertEpsgNameAndIdentifierEqual("Black Sea", 5134, crs.getDatum());
@@ -410,8 +396,7 @@ public final class EPSGFactoryTest extends TestCase {
      */
     @Test
     public void testCompound() throws FactoryException {
-        final EPSGFactory factory = TestFactorySource.factory;
-        assumeTrue(factory != null);
+        final EPSGFactory factory = dataEPSG.factory();
         final CompoundCRS crs = factory.createCompoundCRS("EPSG:7400");
         assertEpsgNameAndIdentifierEqual("NTF (Paris) + NGF IGN69 height", 
7400, crs);
 
@@ -440,8 +425,7 @@ public final class EPSGFactoryTest extends TestCase {
      */
     @Test
     public void testDeprecatedCoordinateSystems() throws FactoryException {
-        final EPSGFactory factory = TestFactorySource.factory;
-        assumeTrue(factory != null);
+        final EPSGFactory factory = dataEPSG.factory();
         for (final Map.Entry<Integer,Integer> entry : 
EPSGDataAccess.deprecatedCS().entrySet()) {
             final CoordinateSystem expected = 
factory.createEllipsoidalCS(entry.getValue().toString());
             loggings.assertNoUnexpectedLog();
@@ -482,9 +466,7 @@ public final class EPSGFactoryTest extends TestCase {
      */
     @Test
     public void testDeprecatedGeographic() throws FactoryException {
-        final EPSGFactory factory = TestFactorySource.factory;
-        assumeTrue(factory != null);
-
+        final EPSGFactory factory = dataEPSG.factory();
         final GeographicCRS crs = factory.createGeographicCRS("63266405");
         assertEpsgNameAndIdentifierEqual("WGS 84 (deg)", 63266405, crs);
         assertAxisDirectionsEqual(crs.getCoordinateSystem(), 
AxisDirection.NORTH, AxisDirection.EAST);
@@ -503,9 +485,7 @@ public final class EPSGFactoryTest extends TestCase {
      */
     @Test
     public void testDeprecatedProjected() throws FactoryException {
-        final EPSGFactory factory = TestFactorySource.factory;
-        assumeTrue(factory != null);
-
+        final EPSGFactory factory = dataEPSG.factory();
         final ProjectedCRS crs = factory.createProjectedCRS("3786");
         assertEpsgNameAndIdentifierEqual("World Equidistant Cylindrical 
(Sphere)", 3786, crs);
         assertEpsgNameAndIdentifierEqual("Equidistant Cylindrical 
(Spherical)", 9823, crs.getConversionFromBase().getMethod());
@@ -538,8 +518,7 @@ public final class EPSGFactoryTest extends TestCase {
      */
     @Test
     public void testCreateByName() throws FactoryException {
-        final EPSGFactory factory = TestFactorySource.factory;
-        assumeTrue(factory != null);
+        final EPSGFactory factory = dataEPSG.factory();
         assertSame   (factory.createUnit("9002"), factory.createUnit("foot"));
         assertNotSame(factory.createUnit("9001"), factory.createUnit("foot"));
         assertSame   (factory.createUnit("9202"), factory.createUnit("ppm"));  
     // Search in alias table.
@@ -567,8 +546,7 @@ public final class EPSGFactoryTest extends TestCase {
      */
     @Test
     public void testAuthorityCodes() throws FactoryException {
-        final EPSGFactory factory = TestFactorySource.factory;
-        assumeTrue(factory != null);
+        final EPSGFactory factory = dataEPSG.factory();
         /*
          * Most basic objects.
          * Note: the numbers in 'size() >= x' checks were determined from the 
content of EPSG dataset version 7.9.
@@ -731,9 +709,7 @@ public final class EPSGFactoryTest extends TestCase {
      */
     @Test
     public void testDescriptionText() throws FactoryException {
-        final EPSGFactory factory = TestFactorySource.factory;
-        assumeTrue(factory != null);
-
+        final EPSGFactory factory = dataEPSG.factory();
         assertEquals("World Geodetic System 1984", factory.getDescriptionText( 
"6326").toString(Locale.US));
         assertEquals("Mean Sea Level",             factory.getDescriptionText( 
"5100").toString(Locale.US));
         assertEquals("NTF (Paris) / Nord France",  
factory.getDescriptionText("27591").toString(Locale.US));
@@ -748,8 +724,7 @@ public final class EPSGFactoryTest extends TestCase {
      */
     @Test
     public void testConversion() throws FactoryException {
-        final EPSGFactory factory = TestFactorySource.factory;
-        assumeTrue(factory != null);
+        final EPSGFactory factory = dataEPSG.factory();
         /*
          * Fetch directly the "UTM zone 10N" operation. Because this operation 
was not obtained in
          * the context of a projected CRS, the source and target CRS shall be 
unspecified (i.e. null).
@@ -806,8 +781,7 @@ public final class EPSGFactoryTest extends TestCase {
      */
     @Test
     public void testSimpleTransformation() throws FactoryException {
-        final EPSGFactory factory = TestFactorySource.factory;
-        assumeTrue(factory != null);
+        final EPSGFactory factory = dataEPSG.factory();
         final CoordinateOperation operation = 
factory.createCoordinateOperation("1764");
         assertEpsgNameAndIdentifierEqual("NTF (Paris) to NTF (2)", 1764, 
operation);
         assertInstanceOf(Transformation.class, operation);
@@ -823,8 +797,7 @@ public final class EPSGFactoryTest extends TestCase {
      */
     @Test
     public void testTransformation() throws FactoryException {
-        final EPSGFactory factory = TestFactorySource.factory;
-        assumeTrue(factory != null);
+        final EPSGFactory factory = dataEPSG.factory();
         final CoordinateOperation operation = 
factory.createCoordinateOperation("1609");
         assertEpsgNameAndIdentifierEqual("BD72 to WGS 84 (1)", 1609, 
operation);
         assertEquals(1.0, ((AbstractCoordinateOperation) 
operation).getLinearAccuracy());
@@ -839,8 +812,7 @@ public final class EPSGFactoryTest extends TestCase {
      */
     @Test
     public void testCreateFromCoordinateReferenceSystemCodes() throws 
FactoryException {
-        final EPSGFactory factory = TestFactorySource.factory;
-        assumeTrue(factory != null);
+        final EPSGFactory factory = dataEPSG.factory();
         /*
          * ED50 (4230)  to  WGS 84 (4326)  using
          * Geocentric translations (9603).
@@ -912,8 +884,7 @@ public final class EPSGFactoryTest extends TestCase {
      */
     @Test
     public void testFindGeographic() throws FactoryException {
-        final EPSGFactory factory = TestFactorySource.factory;
-        assumeTrue(factory != null);
+        final EPSGFactory factory = dataEPSG.factory();
         final IdentifiedObjectFinder finder = 
factory.newIdentifiedObjectFinder();
         final DefaultGeographicCRS crs = (DefaultGeographicCRS) CRS.fromWKT(
                 "GEOGCS[“WGS 84”,\n" +
@@ -964,8 +935,7 @@ public final class EPSGFactoryTest extends TestCase {
      */
     @Test
     public void testFindProjected() throws FactoryException {
-        final EPSGFactory factory = TestFactorySource.factory;
-        assumeTrue(factory != null);
+        final EPSGFactory factory = dataEPSG.factory();
         final IdentifiedObjectFinder finder = 
factory.newIdentifiedObjectFinder();
         /*
          * The PROJCS below intentionally uses a name different from the one 
found in the
diff --git 
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/CoordinateOperationFinderTest.java
 
b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/CoordinateOperationFinderTest.java
index 6c636deede..5e2dd7e319 100644
--- 
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/CoordinateOperationFinderTest.java
+++ 
b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/CoordinateOperationFinderTest.java
@@ -59,9 +59,9 @@ import static 
org.apache.sis.referencing.util.Formulas.ANGULAR_TOLERANCE;
 import static 
org.apache.sis.referencing.util.PositionalAccuracyConstant.DATUM_SHIFT_APPLIED;
 
 // Test dependencies
-import org.junit.jupiter.api.BeforeAll;
-import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestInstance;
 import static org.junit.jupiter.api.Assertions.*;
 import org.apache.sis.test.TestUtilities;
 import org.apache.sis.referencing.cs.HardCodedCS;
@@ -80,30 +80,17 @@ import org.opengis.test.Assertions;
  *
  * @author  Martin Desruisseaux (Geomatys)
  */
+@TestInstance(TestInstance.Lifecycle.PER_CLASS)
 public final class CoordinateOperationFinderTest extends MathTransformTestCase 
{
     /**
      * The transformation factory to use for testing.
      */
-    private static DefaultCoordinateOperationFactory factory;
+    private final DefaultCoordinateOperationFactory factory;
 
     /**
      * The parser to use for WKT strings used in this test.
      */
-    private static WKTFormat parser;
-
-    /**
-     * The instance on which to execute the tests.
-     */
-    private CoordinateOperationFinder finder;
-
-    /**
-     * Creates a new test case.
-     *
-     * @throws FactoryException if an error occurred while initializing the 
finder to test.
-     */
-    public CoordinateOperationFinderTest() throws FactoryException {
-        finder = new CoordinateOperationFinder(null, factory, null);
-    }
+    private final WKTFormat parser;
 
     /**
      * Creates a new {@link DefaultCoordinateOperationFactory} to use for 
testing purpose.
@@ -111,8 +98,7 @@ public final class CoordinateOperationFinderTest extends 
MathTransformTestCase {
      *
      * @throws ParseException if an error occurred while preparing the WKT 
parser.
      */
-    @BeforeAll
-    public static void createFactory() throws ParseException {
+    public CoordinateOperationFinderTest() throws ParseException {
         factory = new DefaultCoordinateOperationFactory();
         parser  = new WKTFormat();
         /*
@@ -139,18 +125,30 @@ public final class CoordinateOperationFinderTest extends 
MathTransformTestCase {
     }
 
     /**
-     * Disposes the factory created by {@link #createFactory()} after all 
tests have been executed.
+     * Resets all fields that may be modified by test methods in this class.
+     * This is needed because we reuse the same instance for all methods,
+     * in order to reuse the factory and parser created in the constructor.
+     */
+    @Override
+    @BeforeEach
+    public void reset() {
+        super.reset();
+        isInverseTransformSupported = true;
+    }
+
+    /**
+     * Returns the instance on which to execute the tests.
+     *
+     * @throws FactoryException if an error occurred while initializing the 
finder to test.
      */
-    @AfterAll
-    public static void disposeFactory() {
-        factory = null;
-        parser  = null;
+    private CoordinateOperationFinder finder() throws FactoryException {
+        return new CoordinateOperationFinder(null, factory, null);
     }
 
     /**
      * Returns the CRS for the given Well Known Text.
      */
-    private static CoordinateReferenceSystem parse(final String wkt) throws 
ParseException {
+    private CoordinateReferenceSystem parse(final String wkt) throws 
ParseException {
         return (CoordinateReferenceSystem) parser.parseObject(wkt);
     }
 
@@ -184,13 +182,12 @@ public final class CoordinateOperationFinderTest extends 
MathTransformTestCase {
      * Implementation of {@link #testIdentityTransform()} using the given CRS.
      */
     private void testIdentityTransform(final CoordinateReferenceSystem crs) 
throws FactoryException {
-        final CoordinateOperation operation = finder.createOperation(crs, crs);
+        final CoordinateOperation operation = finder().createOperation(crs, 
crs);
         assertSame(crs, operation.getSourceCRS());
         assertSame(crs, operation.getTargetCRS());
         assertTrue(operation.getMathTransform().isIdentity());
         assertTrue(operation.getCoordinateOperationAccuracy().isEmpty());
         assertInstanceOf(Conversion.class, operation);
-        finder = new CoordinateOperationFinder(null, factory, null);        // 
Reset for next call.
     }
 
     /**
@@ -263,7 +260,7 @@ public final class CoordinateOperationFinderTest extends 
MathTransformTestCase {
             final GeographicCRS sourceCRS, final GeographicCRS targetCRS)
             throws ParseException, FactoryException, TransformException
     {
-        final CoordinateOperation operation = 
finder.createOperation(sourceCRS, targetCRS);
+        final CoordinateOperation operation = 
finder().createOperation(sourceCRS, targetCRS);
         assertSame(sourceCRS, operation.getSourceCRS());
         assertSame(targetCRS, operation.getTargetCRS());
         assertFalse(operation.getMathTransform().isIdentity());
@@ -322,7 +319,7 @@ public final class CoordinateOperationFinderTest extends 
MathTransformTestCase {
                 "  Id[“EPSG”, “4807”]]");
 
         final GeographicCRS       targetCRS = CommonCRS.WGS84.geographic();
-        final CoordinateOperation operation = 
finder.createOperation(sourceCRS, targetCRS);
+        final CoordinateOperation operation = 
finder().createOperation(sourceCRS, targetCRS);
 
         assertSame(sourceCRS, operation.getSourceCRS());
         assertSame(targetCRS, operation.getTargetCRS());
@@ -367,7 +364,7 @@ public final class CoordinateOperationFinderTest extends 
MathTransformTestCase {
                 "    Unit[“kilometre”, 1000]]");
 
         final GeocentricCRS       targetCRS = CommonCRS.WGS84.geocentric();
-        final CoordinateOperation operation = 
finder.createOperation(sourceCRS, targetCRS);
+        final CoordinateOperation operation = 
finder().createOperation(sourceCRS, targetCRS);
 
         assertSame(sourceCRS, operation.getSourceCRS());
         assertSame(targetCRS, operation.getTargetCRS());
@@ -420,7 +417,7 @@ public final class CoordinateOperationFinderTest extends 
MathTransformTestCase {
                                           final CoordinateReferenceSystem 
targetCRS)
             throws FactoryException
     {
-        final CoordinateOperation operation = 
finder.createOperation(sourceCRS, targetCRS);
+        final CoordinateOperation operation = 
finder().createOperation(sourceCRS, targetCRS);
         assertSame(sourceCRS, operation.getSourceCRS());
         assertSame(targetCRS, operation.getTargetCRS());
         assertEquals("Geocentric conversion", operation.getName().getCode());
@@ -450,7 +447,7 @@ public final class CoordinateOperationFinderTest extends 
MathTransformTestCase {
                 "    Axis[“y”, NORTH],\n" +
                 "    Unit[“US survey foot”, 0.304800609601219]]");
 
-        final CoordinateOperation operation = 
finder.createOperation(sourceCRS, targetCRS);
+        final CoordinateOperation operation = 
finder().createOperation(sourceCRS, targetCRS);
         assertSame(sourceCRS, operation.getSourceCRS());
         assertSame(targetCRS, operation.getTargetCRS());
         assertEquals("TM", operation.getName().getCode());
@@ -492,7 +489,7 @@ public final class CoordinateOperationFinderTest extends 
MathTransformTestCase {
     public void testPositionVectorTransformation() throws ParseException, 
FactoryException, TransformException {
         final CoordinateReferenceSystem sourceCRS = 
CommonCRS.WGS84.geographic();
         final CoordinateReferenceSystem targetCRS = parse(AGD66());
-        final CoordinateOperation operation = 
finder.createOperation(sourceCRS, targetCRS);
+        final CoordinateOperation operation = 
finder().createOperation(sourceCRS, targetCRS);
         transform  = operation.getMathTransform();
         tolerance  = LINEAR_TOLERANCE;
         λDimension = new int[] {0};
@@ -589,7 +586,7 @@ public final class CoordinateOperationFinderTest extends 
MathTransformTestCase {
          * If no datum shift is applied, the point will be at 191 metres from
          * expected value.
          */
-        final CoordinateOperation operation = 
finder.createOperation(sourceCRS, targetCRS);
+        final CoordinateOperation operation = 
finder().createOperation(sourceCRS, targetCRS);
         tolerance = LINEAR_TOLERANCE;
         transform = operation.getMathTransform();
         verifyTransform(new double[] {926713.702, 7348947.026},
@@ -617,7 +614,7 @@ public final class CoordinateOperationFinderTest extends 
MathTransformTestCase {
     public void testIncompatibleVerticalCRS() throws FactoryException {
         final VerticalCRS sourceCRS = CommonCRS.Vertical.NAVD88.crs();
         final VerticalCRS targetCRS = CommonCRS.Vertical.MEAN_SEA_LEVEL.crs();
-        var e = assertThrows(OperationNotFoundException.class, () -> 
finder.createOperation(sourceCRS, targetCRS));
+        var e = assertThrows(OperationNotFoundException.class, () -> 
finder().createOperation(sourceCRS, targetCRS));
         assertMessageContains(e, "North American Vertical Datum", "Mean Sea 
Level");
     }
 
@@ -633,7 +630,7 @@ public final class CoordinateOperationFinderTest extends 
MathTransformTestCase {
     public void testTemporalConversion() throws FactoryException, 
TransformException {
         final TemporalCRS sourceCRS = CommonCRS.Temporal.UNIX.crs();
         final TemporalCRS targetCRS = CommonCRS.Temporal.MODIFIED_JULIAN.crs();
-        final CoordinateOperation operation = 
finder.createOperation(sourceCRS, targetCRS);
+        final CoordinateOperation operation = 
finder().createOperation(sourceCRS, targetCRS);
         assertSame(sourceCRS, operation.getSourceCRS());
         assertSame(targetCRS, operation.getTargetCRS());
         assertEquals("Axis changes", operation.getName().getCode());
@@ -671,7 +668,7 @@ public final class CoordinateOperationFinderTest extends 
MathTransformTestCase {
         // NOTE: make sure that the 'sourceCRS' below is not equal to any 
other 'sourceCRS' created in this class.
         final CompoundCRS   sourceCRS = compound("Test4D", 
CommonCRS.WGS84.geographic3D(), CommonCRS.Temporal.UNIX.crs());
         final GeographicCRS targetCRS = CommonCRS.WGS84.geographic();
-        final CoordinateOperation operation = 
finder.createOperation(sourceCRS, targetCRS);
+        final CoordinateOperation operation = 
finder().createOperation(sourceCRS, targetCRS);
         assertSame(sourceCRS, operation.getSourceCRS());
         assertSame(targetCRS, operation.getTargetCRS());
 
@@ -706,7 +703,7 @@ public final class CoordinateOperationFinderTest extends 
MathTransformTestCase {
     public void testGeographic3D_to_2D() throws FactoryException, 
TransformException {
         final GeographicCRS sourceCRS = CommonCRS.WGS84.geographic3D();
         final GeographicCRS targetCRS = CommonCRS.WGS84.geographic();
-        final CoordinateOperation operation = 
finder.createOperation(sourceCRS, targetCRS);
+        final CoordinateOperation operation = 
finder().createOperation(sourceCRS, targetCRS);
         assertSame(sourceCRS, operation.getSourceCRS());
         assertSame(targetCRS, operation.getTargetCRS());
         assertEquals("Axis changes", operation.getName().getCode());
@@ -747,7 +744,7 @@ public final class CoordinateOperationFinderTest extends 
MathTransformTestCase {
     public void testGeographic2D_to_3D() throws FactoryException, 
TransformException {
         final GeographicCRS sourceCRS = CommonCRS.WGS84.geographic();
         final GeographicCRS targetCRS = CommonCRS.WGS84.geographic3D();
-        final CoordinateOperation operation = 
finder.createOperation(sourceCRS, targetCRS);
+        final CoordinateOperation operation = 
finder().createOperation(sourceCRS, targetCRS);
         assertSame(sourceCRS, operation.getSourceCRS());
         assertSame(targetCRS, operation.getTargetCRS());
         assertEquals("Axis changes", operation.getName().getCode());
@@ -789,7 +786,7 @@ public final class CoordinateOperationFinderTest extends 
MathTransformTestCase {
     public void testGeographic3D_to_EllipsoidalHeight() throws 
FactoryException, TransformException {
         final CoordinateReferenceSystem sourceCRS = 
CommonCRS.WGS84.geographic3D();
         final CoordinateReferenceSystem targetCRS = 
HardCodedCRS.ELLIPSOIDAL_HEIGHT_cm;
-        final CoordinateOperation operation = 
finder.createOperation(sourceCRS, targetCRS);
+        final CoordinateOperation operation = 
finder().createOperation(sourceCRS, targetCRS);
         assertSame(sourceCRS, operation.getSourceCRS());
         assertSame(targetCRS, operation.getTargetCRS());
         assertEquals("Axis changes", operation.getName().getCode());
@@ -827,7 +824,7 @@ public final class CoordinateOperationFinderTest extends 
MathTransformTestCase {
         // NOTE: make sure that the 'sourceCRS' below is not equal to any 
other 'sourceCRS' created in this class.
         final CompoundCRS sourceCRS = compound("Test4D", 
CommonCRS.WGS84.geographic3D(), CommonCRS.Temporal.JULIAN.crs());
         final VerticalCRS targetCRS = CommonCRS.Vertical.ELLIPSOIDAL.crs();
-        final CoordinateOperation operation = 
finder.createOperation(sourceCRS, targetCRS);
+        final CoordinateOperation operation = 
finder().createOperation(sourceCRS, targetCRS);
         assertSame(sourceCRS, operation.getSourceCRS());
         assertSame(targetCRS, operation.getTargetCRS());
         assertEquals("Axis changes", operation.getName().getCode());
@@ -895,7 +892,7 @@ public final class CoordinateOperationFinderTest extends 
MathTransformTestCase {
         sourceCRS = compound("Mercator 3D", sourceCRS, 
CommonCRS.Vertical.MEAN_SEA_LEVEL.crs());
         sourceCRS = compound("Mercator 4D", sourceCRS, 
CommonCRS.Temporal.MODIFIED_JULIAN.crs());
 
-        final CoordinateOperation operation = 
finder.createOperation(sourceCRS, targetCRS);
+        final CoordinateOperation operation = 
finder().createOperation(sourceCRS, targetCRS);
         assertSame(sourceCRS, operation.getSourceCRS());
         assertSame(targetCRS, operation.getTargetCRS());
 
@@ -937,7 +934,7 @@ public final class CoordinateOperationFinderTest extends 
MathTransformTestCase {
         // NOTE: make sure that the 'sourceCRS' below is not equal to any 
other 'sourceCRS' created in this class.
         final CompoundCRS sourceCRS = compound("Test3D", 
CommonCRS.WGS84.geographic(),   CommonCRS.Temporal.UNIX.crs());
         final CompoundCRS targetCRS = compound("Test4D", 
CommonCRS.WGS84.geographic3D(), CommonCRS.Temporal.MODIFIED_JULIAN.crs());
-        final CoordinateOperation operation = 
finder.createOperation(sourceCRS, targetCRS);
+        final CoordinateOperation operation = 
finder().createOperation(sourceCRS, targetCRS);
         assertSame(sourceCRS, operation.getSourceCRS());
         assertSame(targetCRS, operation.getTargetCRS());
         assertInstanceOf(ConcatenatedOperation.class, operation);
@@ -986,7 +983,7 @@ public final class CoordinateOperationFinderTest extends 
MathTransformTestCase {
                     0,   0,   1
                 })), HardCodedCS.DISPLAY);
 
-        final CoordinateOperation operation = 
finder.createOperation(sourceCRS, targetCRS);
+        final CoordinateOperation operation = 
finder().createOperation(sourceCRS, targetCRS);
         assertSame(sourceCRS, operation.getSourceCRS());
         assertSame(targetCRS, operation.getTargetCRS());
 
@@ -1010,6 +1007,7 @@ public final class CoordinateOperationFinderTest extends 
MathTransformTestCase {
     public void testEngineeringCRS() throws FactoryException {
         final DefaultEngineeringCRS sourceCRS = createEngineering("Screen 
display", AxisDirection.DISPLAY_DOWN);
         final DefaultEngineeringCRS targetCRS = createEngineering("Another 
device", AxisDirection.DISPLAY_DOWN);
+        final CoordinateOperationFinder finder = finder();
         var e = assertThrows(OperationNotFoundException.class, () -> 
finder.createOperation(sourceCRS, targetCRS),
                 "Should not create operation between CRS of different datum.");
         assertMessageContains(e, "A test CRS");
diff --git 
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/CoordinateOperationRegistryTest.java
 
b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/CoordinateOperationRegistryTest.java
index 25874cbfce..3fa8e71b87 100644
--- 
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/CoordinateOperationRegistryTest.java
+++ 
b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/CoordinateOperationRegistryTest.java
@@ -37,9 +37,9 @@ import org.apache.sis.io.wkt.WKTFormat;
 import org.apache.sis.referencing.operation.transform.MathTransformTestCase;
 
 // Test dependencies
-import org.junit.jupiter.api.BeforeAll;
-import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestInstance;
 import static org.junit.jupiter.api.Assumptions.assumeTrue;
 import static org.junit.jupiter.api.Assertions.*;
 import static 
org.apache.sis.referencing.Assertions.assertEpsgNameAndIdentifierEqual;
@@ -67,16 +67,17 @@ import org.opengis.metadata.Identifier;
  *
  * @author  Martin Desruisseaux (Geomatys)
  */
+@TestInstance(TestInstance.Lifecycle.PER_CLASS)
 public final class CoordinateOperationRegistryTest extends 
MathTransformTestCase {
     /**
      * The transformation factory to use for testing.
      */
-    private static DefaultCoordinateOperationFactory factory;
+    private final DefaultCoordinateOperationFactory factory;
 
     /**
      * The parser to use for WKT strings used in this test.
      */
-    private static WKTFormat parser;
+    private final WKTFormat parser;
 
     /**
      * The EPSG authority factory for CRS objects. Can be used as an 
alternative to {@link #parser}.
@@ -88,46 +89,40 @@ public final class CoordinateOperationRegistryTest extends 
MathTransformTestCase
      */
     private final CoordinateOperationRegistry registry;
 
-    /**
-     * Creates a new test case.
-     *
-     * @throws FactoryException if an error occurred while creating the 
factory to be tested.
-     */
-    public CoordinateOperationRegistryTest() throws FactoryException {
-        crsFactory = CRS.getAuthorityFactory("EPSG");
-        assumeTrue(crsFactory instanceof CoordinateOperationAuthorityFactory, 
"EPSG factory required.");
-        registry = new 
CoordinateOperationRegistry((CoordinateOperationAuthorityFactory) crsFactory, 
factory, null);
-    }
-
     /**
      * Creates a new {@link DefaultCoordinateOperationFactory} to use for 
testing purpose.
      * The same factory will be used for all tests in this class.
      *
      * @throws ParseException if an error occurred while preparing the WKT 
parser.
+     * @throws FactoryException if an error occurred while creating the 
factory to be tested.
      */
-    @BeforeAll
-    public static void createFactory() throws ParseException {
+    public CoordinateOperationRegistryTest() throws ParseException, 
FactoryException {
+        crsFactory = CRS.getAuthorityFactory("EPSG");
+        assumeTrue(crsFactory instanceof CoordinateOperationAuthorityFactory, 
"EPSG factory required.");
         factory = new DefaultCoordinateOperationFactory();
         parser  = new WKTFormat();
         parser.addFragment("NTF",
                 "Datum[“Nouvelle Triangulation Française (Paris)”,\n" +
                 "  Ellipsoid[“Clarke 1880 (IGN)”, 6378249.2, 
293.4660212936269]]");
+        registry = new 
CoordinateOperationRegistry((CoordinateOperationAuthorityFactory) crsFactory, 
factory, null);
     }
 
     /**
-     * Disposes the factory created by {@link #createFactory()} after all 
tests have been executed.
+     * Resets all fields that may be modified by test methods in this class.
+     * This is needed because we reuse the same instance for all methods,
+     * in order to reuse the factory and parser created in the constructor.
      */
-    @AfterAll
-    public static void disposeFactory() {
-        factory = null;
-        parser  = null;
+    @Override
+    @BeforeEach
+    public void reset() {
+        super.reset();
     }
 
     /**
      * Returns the CRS for the given Well Known Text.
      */
-    private static CoordinateReferenceSystem parse(final String wkt) throws 
ParseException {
-        return (CoordinateReferenceSystem) parser.parseObject(wkt);
+    private CoordinateReferenceSystem parse(final String wkt) throws 
ParseException {
+        return assertInstanceOf(CoordinateReferenceSystem.class, 
parser.parseObject(wkt));
     }
 
     /**
diff --git 
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/DefaultCoordinateOperationFactoryTest.java
 
b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/DefaultCoordinateOperationFactoryTest.java
index e1229fee8b..d43ed1e1fd 100644
--- 
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/DefaultCoordinateOperationFactoryTest.java
+++ 
b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/DefaultCoordinateOperationFactoryTest.java
@@ -37,9 +37,9 @@ import org.apache.sis.io.wkt.WKTFormat;
 import org.apache.sis.referencing.operation.transform.MathTransformTestCase;
 
 // Test dependencies
-import org.junit.jupiter.api.BeforeAll;
-import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestInstance;
 import static org.junit.jupiter.api.Assertions.*;
 import static org.apache.sis.test.Assertions.assertMessageContains;
 import static 
org.apache.sis.referencing.Assertions.assertEpsgNameAndIdentifierEqual;
@@ -57,22 +57,17 @@ import static 
org.apache.sis.referencing.Assertions.assertEpsgNameAndIdentifierE
  *
  * @author  Martin Desruisseaux (Geomatys)
  */
+@TestInstance(TestInstance.Lifecycle.PER_CLASS)
 public final class DefaultCoordinateOperationFactoryTest extends 
MathTransformTestCase {
     /**
      * The transformation factory to use for testing.
      */
-    private static DefaultCoordinateOperationFactory factory;
+    private final DefaultCoordinateOperationFactory factory;
 
     /**
      * The parser to use for WKT strings used in this test.
      */
-    private static WKTFormat parser;
-
-    /**
-     * Creates a new test case.
-     */
-    public DefaultCoordinateOperationFactoryTest() {
-    }
+    private final WKTFormat parser;
 
     /**
      * Creates a new {@link DefaultCoordinateOperationFactory} to use for 
testing purpose.
@@ -80,8 +75,7 @@ public final class DefaultCoordinateOperationFactoryTest 
extends MathTransformTe
      *
      * @throws ParseException if an error occurred while preparing the WKT 
parser.
      */
-    @BeforeAll
-    public static void createFactory() throws ParseException {
+    public DefaultCoordinateOperationFactoryTest() throws ParseException {
         factory = new DefaultCoordinateOperationFactory();
         parser  = new WKTFormat();
         parser.addFragment("NTF",
@@ -119,19 +113,22 @@ public final class DefaultCoordinateOperationFactoryTest 
extends MathTransformTe
     }
 
     /**
-     * Disposes the factory created by {@link #createFactory()} after all 
tests have been executed.
+     * Resets all fields that may be modified by test methods in this class.
+     * This is needed because we reuse the same instance for all test methods,
+     * in order to reuse the factory and parser created in the constructor.
      */
-    @AfterAll
-    public static void disposeFactory() {
-        factory = null;
-        parser  = null;
+    @Override
+    @BeforeEach
+    public void reset() {
+        super.reset();
+        isInverseTransformSupported = true;
     }
 
     /**
      * Returns the CRS for the given Well Known Text.
      */
-    private static CoordinateReferenceSystem parse(final String wkt) throws 
ParseException {
-        return (CoordinateReferenceSystem) parser.parseObject(wkt);
+    private CoordinateReferenceSystem parse(final String wkt) throws 
ParseException {
+        return assertInstanceOf(CoordinateReferenceSystem.class, 
parser.parseObject(wkt));
     }
 
     /**
diff --git 
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/transform/CoordinateSystemTransformTest.java
 
b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/transform/CoordinateSystemTransformTest.java
index 91c398ade4..d44adc9ded 100644
--- 
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/transform/CoordinateSystemTransformTest.java
+++ 
b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/transform/CoordinateSystemTransformTest.java
@@ -30,9 +30,9 @@ import org.apache.sis.measure.Units;
 import org.apache.sis.util.ArraysExt;
 
 // Test dependencies
-import org.junit.jupiter.api.BeforeAll;
-import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestInstance;
 import static org.junit.jupiter.api.Assertions.*;
 import org.opengis.test.referencing.TransformTestCase;
 import org.apache.sis.referencing.cs.HardCodedCS;
@@ -43,34 +43,28 @@ import org.apache.sis.referencing.cs.HardCodedCS;
  *
  * @author  Martin Desruisseaux (Geomatys)
  */
+@TestInstance(TestInstance.Lifecycle.PER_CLASS)
 public final class CoordinateSystemTransformTest extends TransformTestCase {
     /**
      * A right-handed spherical coordinate system.
      */
-    private static SphericalCS spherical;
+    private final SphericalCS spherical;
 
     /**
      * The factory to use for creating the affine transforms and concatenated 
transforms.
      */
-    private static MathTransformFactory factory;
+    private final MathTransformFactory factory;
 
     /**
      * The operation method used.
      */
-    private static ThreadLocal<OperationMethod> lastMethod;
-
-    /**
-     * Creates a new test case.
-     */
-    public CoordinateSystemTransformTest() {
-    }
+    private final ThreadLocal<OperationMethod> lastMethod;
 
     /**
      * Creates the {@link MathTransformFactory} to be used for the tests.
      * We do not use the system-wide factory in order to have better tests 
isolation.
      */
-    @BeforeAll
-    public static void createFactory() {
+    public CoordinateSystemTransformTest() {
         factory = new DefaultMathTransformFactory();
         spherical = (SphericalCS) 
DefaultGeocentricCRS.castOrCopy(CommonCRS.WGS84.spherical())
                             
.forConvention(AxesConvention.RIGHT_HANDED).getCoordinateSystem();
@@ -78,13 +72,15 @@ public final class CoordinateSystemTransformTest extends 
TransformTestCase {
     }
 
     /**
-     * Disposes the {@link MathTransformFactory} used for the tests.
+     * Resets all fields that may be modified by test methods in this class.
+     * This is needed because we reuse the same instance for all methods,
+     * in order to reuse the factory and parser created in the constructor.
      */
-    @AfterAll
-    public static void disposeFactory() {
-        spherical  = null;
-        factory    = null;
-        lastMethod = null;
+    @BeforeEach
+    public void reset() {
+        transform = null;
+        lastMethod.remove();
+        tolerance = 0;
     }
 
     /**
@@ -123,7 +119,7 @@ public final class CoordinateSystemTransformTest extends 
TransformTestCase {
     /**
      * Verifies that {@link #lastMethod} has the expected value.
      */
-    private static void assertMethodEquals(final String expected) {
+    private void assertMethodEquals(final String expected) {
         final OperationMethod method = lastMethod.get();
         assertNotNull(method);
         assertEquals(expected, method.getName().getCode());
@@ -139,7 +135,6 @@ public final class CoordinateSystemTransformTest extends 
TransformTestCase {
     @Test
     public void testSphericalToSpherical() throws FactoryException, 
TransformException {
         createTransform(HardCodedCS.SPHERICAL, spherical);
-        tolerance = 0;
         final double[][] data = SphericalToCartesianTest.testData();
         final double[] source = data[0];
         final double[] target = data[1];
diff --git 
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/transform/MathTransformTestCase.java
 
b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/transform/MathTransformTestCase.java
index ddd5fb891d..3579e088b0 100644
--- 
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/transform/MathTransformTestCase.java
+++ 
b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/transform/MathTransformTestCase.java
@@ -71,6 +71,13 @@ import org.opengis.test.referencing.TransformTestCase;
  *   <li>{@link #verifyTransform(double[], double[])}   — from GeoAPI and 
Apache SIS</li>
  * </ul>
  *
+ * <h2>Life cycle</h2>
+ * GeoAPI tests have a state. Therefor, a new instance of {@code 
MathTransformTestCase} should be created
+ * for each test method to execute. However, it may be costly if the 
constructor requires large resources
+ * (e.g., a connection to an EPSG database). For making easier to reuse the 
same {@code TestCase} instance,
+ * a {@link #reset()} method is provided. Subclasses are responsible to 
override that method with the
+ * {@link org.junit.jupiter.api.BeforeEach} annotation if they want per-class 
life cycle.
+ *
  * @author  Martin Desruisseaux (Geomatys)
  */
 public abstract class MathTransformTestCase extends TransformTestCase {
@@ -124,16 +131,34 @@ public abstract class MathTransformTestCase extends 
TransformTestCase {
         /*
          * Use 'zTolerance' threshold instead of 'tolerance' when comparing 
vertical coordinate values.
          */
-        toleranceModifier = (final double[] tolerance, final DirectPosition 
coordinate, final CalculationType mode) -> {
+        toleranceModifier = (final double[] tolerances, final DirectPosition 
coordinates, final CalculationType mode) -> {
             if (mode != CalculationType.IDENTITY) {
                 final int i = forComparison(zDimension, mode);
-                if (i >= 0 && i < tolerance.length) {
-                    tolerance[i] = zTolerance;
+                if (i >= 0 && i < tolerances.length) {
+                    tolerances[i] = zTolerance;
                 }
             }
         };
     }
 
+    /**
+     * Resets all fields that may be modified by test methods in this class.
+     * This is needed if the subclass reuses the same {@code TestCase} 
instance for all test methods.
+     *
+     * <p>The default implementation of this method does not reset the {@code 
isFooSupported} Boolean flags,
+     * on the assumption that subclasses do not modify them. Subclasses are 
responsible for restoring these
+     * flags if needed.</p>
+     */
+    protected void reset() {
+        transform  = null;
+        λDimension = null;
+        zDimension = null;
+        zTolerance = 0;
+        tolerance  = 0;
+        derivativeDeltas = null;
+        configurationTip = null;
+    }
+
     /**
      * Returns the value to use from the {@link #λDimension} or {@link 
zDimension} for the
      * given comparison mode, or -1 if none.
diff --git 
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/test/integration/CoordinateReferenceSystemTest.java
 
b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/test/integration/CoordinateReferenceSystemTest.java
index 56854e95d3..d74ea2bf71 100644
--- 
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/test/integration/CoordinateReferenceSystemTest.java
+++ 
b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/test/integration/CoordinateReferenceSystemTest.java
@@ -24,13 +24,12 @@ import org.opengis.referencing.crs.GeodeticCRS;
 import org.opengis.referencing.crs.GeneralDerivedCRS;
 import org.opengis.referencing.crs.CoordinateReferenceSystem;
 import org.apache.sis.referencing.CRS;
-import org.apache.sis.referencing.factory.TestFactorySource;
 
 // Test dependencies
 import org.junit.jupiter.api.Test;
-import static org.junit.jupiter.api.Assumptions.assumeTrue;
 import static org.junit.jupiter.api.Assertions.*;
 import org.apache.sis.test.TestCase;
+import org.apache.sis.referencing.factory.TestFactorySource;
 
 
 /**
@@ -53,7 +52,7 @@ public final class CoordinateReferenceSystemTest extends 
TestCase {
      */
     @Test
     public void testCreateFromCombinedURN() throws FactoryException {
-        assumeTrue(TestFactorySource.getSharedFactory() != null);
+        assertTrue(TestFactorySource.getSharedFactory() != null);
         CoordinateReferenceSystem crs = CRS.forCode("urn:ogc:def:crs, 
crs:EPSG::27700, crs:EPSG::5701");
         assertSame(CRS.forCode("EPSG:7405"), crs, "OSGB 1936 / British 
National Grid + ODN height");
     }
@@ -65,7 +64,7 @@ public final class CoordinateReferenceSystemTest extends 
TestCase {
      */
     @Test
     public void testDerivedCRS() throws FactoryException {
-        assumeTrue(TestFactorySource.getSharedFactory() != null);
+        assertTrue(TestFactorySource.getSharedFactory() != null);
         CoordinateReferenceSystem crs = CRS.forCode("EPSG:5820");
         assertInstanceOf(DerivedCRS .class, crs);
         assertInstanceOf(GeodeticCRS.class, crs);
diff --git 
a/endorsed/src/org.apache.sis.storage.geotiff/test/org/apache/sis/storage/geotiff/SelfConsistencyTest.java
 
b/endorsed/src/org.apache.sis.storage.geotiff/test/org/apache/sis/storage/geotiff/SelfConsistencyTest.java
index 1eff9e411c..8cf7796f26 100644
--- 
a/endorsed/src/org.apache.sis.storage.geotiff/test/org/apache/sis/storage/geotiff/SelfConsistencyTest.java
+++ 
b/endorsed/src/org.apache.sis.storage.geotiff/test/org/apache/sis/storage/geotiff/SelfConsistencyTest.java
@@ -20,17 +20,16 @@ import java.util.List;
 import java.util.Optional;
 import java.nio.file.Path;
 import org.opengis.util.GenericName;
+import org.apache.sis.util.Workaround;
 import org.apache.sis.storage.DataStoreException;
 import org.apache.sis.storage.GridCoverageResource;
 import org.apache.sis.storage.IllegalNameException;
 import org.apache.sis.storage.StorageConnector;
 
 // Test dependencies
-import org.junit.jupiter.api.AfterAll;
-import org.junit.jupiter.api.BeforeAll;
 import org.junit.jupiter.api.Test;
-import static org.junit.jupiter.api.Assumptions.assumeTrue;
 import static org.junit.jupiter.api.Assertions.*;
+import static org.junit.jupiter.api.Assumptions.abort;
 import static org.apache.sis.test.Assertions.assertMessageContains;
 import org.apache.sis.test.OptionalTestData;
 import org.apache.sis.storage.test.CoverageReadConsistency;
@@ -46,47 +45,37 @@ import org.apache.sis.storage.test.CoverageReadConsistency;
  * @author  Martin Desruisseaux (Geomatys)
  * @author  Alexis Manin (Geomatys)
  */
-public final class SelfConsistencyTest extends CoverageReadConsistency {
-    /**
-     * The file used for the test, opened only once.
-     */
-    private static GeoTiffStore store;
-
+public final class SelfConsistencyTest extends 
CoverageReadConsistency<GeoTiffStore> {
     /**
      * Opens the test file to be used for all tests.
      *
      * @throws DataStoreException if an error occurred while opening the file.
      */
-    @BeforeAll
-    public static void openFile() throws DataStoreException {
-        final Optional<Path> path = OptionalTestData.GEOTIFF.path();
-        if (path.isPresent()) {
-            store = new GeoTiffStore(null, new StorageConnector(path.get()));
-        }
-        assumeTrue(store != null, "Test file not found.");
+    public SelfConsistencyTest() throws DataStoreException {
+        super(openFile());
     }
 
     /**
-     * Closes the test file used by all tests.
-     *
-     * @throws DataStoreException if an error occurred while closing the file.
+     * Work around for RFE #4093999 in Sun's bug database
+     * ("Relax constraint on placement of this()/super() call in 
constructors").
      */
-    @AfterAll
-    public static void closeFile() throws DataStoreException {
-        final GeoTiffStore s = store;
-        if (s != null) {
-            store = null;       // Clear first in case of failure.
-            s.close();
+    @Workaround(library="JDK", version="1.7")
+    private static GeoTiffStore openFile() throws DataStoreException {
+        final Optional<Path> path = OptionalTestData.GEOTIFF.path();
+        if (path.isPresent()) {
+            return new GeoTiffStore(null, new StorageConnector(path.get()));
         }
+        abort("Test file not found.");
+        return null;
     }
 
     /**
-     * Creates a new test case.
-     *
-     * @throws DataStoreException if an error occurred while fetching the 
first image.
+     * Work around for RFE #4093999 in Sun's bug database
+     * ("Relax constraint on placement of this()/super() call in 
constructors").
      */
-    public SelfConsistencyTest() throws DataStoreException {
-        super(store.components().iterator().next());
+    @Override
+    protected GridCoverageResource resource() throws DataStoreException {
+        return store.components().iterator().next();
     }
 
     /**
diff --git 
a/endorsed/src/org.apache.sis.storage.netcdf/test/org/apache/sis/storage/netcdf/SelfConsistencyTest.java
 
b/endorsed/src/org.apache.sis.storage.netcdf/test/org/apache/sis/storage/netcdf/SelfConsistencyTest.java
index 87d77247a6..9c6ad1d2b5 100644
--- 
a/endorsed/src/org.apache.sis.storage.netcdf/test/org/apache/sis/storage/netcdf/SelfConsistencyTest.java
+++ 
b/endorsed/src/org.apache.sis.storage.netcdf/test/org/apache/sis/storage/netcdf/SelfConsistencyTest.java
@@ -18,14 +18,14 @@ package org.apache.sis.storage.netcdf;
 
 import java.util.Optional;
 import java.nio.file.Path;
+import org.apache.sis.util.Workaround;
 import org.apache.sis.storage.DataStoreException;
 import org.apache.sis.storage.GridCoverageResource;
 import org.apache.sis.storage.StorageConnector;
 
 // Test dependencies
-import org.junit.jupiter.api.AfterAll;
-import org.junit.jupiter.api.BeforeAll;
-import static org.junit.jupiter.api.Assumptions.assumeTrue;
+import static org.junit.jupiter.api.Assertions.*;
+import static org.junit.jupiter.api.Assumptions.abort;
 import org.apache.sis.test.OptionalTestData;
 import org.apache.sis.storage.test.CoverageReadConsistency;
 
@@ -39,46 +39,36 @@ import org.apache.sis.storage.test.CoverageReadConsistency;
  *
  * @author  Martin Desruisseaux (Geomatys)
  */
-public final class SelfConsistencyTest extends CoverageReadConsistency {
-    /**
-     * The file used for the test, opened only once.
-     */
-    private static NetcdfStore store;
-
+public final class SelfConsistencyTest extends 
CoverageReadConsistency<NetcdfStore> {
     /**
      * Opens the test file to be used for all tests.
      *
      * @throws DataStoreException if an error occurred while opening the file.
      */
-    @BeforeAll
-    public static void openFile() throws DataStoreException {
-        final Optional<Path> path = OptionalTestData.NETCDF.path();
-        if (path.isPresent()) {
-            store = new NetcdfStore(null, new StorageConnector(path.get()));
-        }
-        assumeTrue(store != null, "Test file not found.");
+    public SelfConsistencyTest() throws DataStoreException {
+        super(openFile());
     }
 
     /**
-     * Closes the test file used by all tests.
-     *
-     * @throws DataStoreException if an error occurred while closing the file.
+     * Work around for RFE #4093999 in Sun's bug database
+     * ("Relax constraint on placement of this()/super() call in 
constructors").
      */
-    @AfterAll
-    public static void closeFile() throws DataStoreException {
-        final NetcdfStore s = store;
-        if (s != null) {
-            store = null;       // Clear first in case of failure.
-            s.close();
+    @Workaround(library="JDK", version="1.7")
+    private static NetcdfStore openFile() throws DataStoreException {
+        final Optional<Path> path = OptionalTestData.NETCDF.path();
+        if (path.isPresent()) {
+            return new NetcdfStore(null, new StorageConnector(path.get()));
         }
+        abort("Test file not found.");
+        return null;
     }
 
     /**
-     * Creates a new test case.
-     *
-     * @throws DataStoreException if an error occurred while fetching the 
first image.
+     * Work around for RFE #4093999 in Sun's bug database
+     * ("Relax constraint on placement of this()/super() call in 
constructors").
      */
-    public SelfConsistencyTest() throws DataStoreException {
-        super((GridCoverageResource) store.components().iterator().next());
+    @Override
+    protected GridCoverageResource resource() throws DataStoreException {
+        return assertInstanceOf(GridCoverageResource.class, 
store.components().iterator().next());
     }
 }
diff --git 
a/endorsed/src/org.apache.sis.storage.xml/main/org/apache/sis/storage/gpx/StoreProvider.java
 
b/endorsed/src/org.apache.sis.storage.xml/main/org/apache/sis/storage/gpx/StoreProvider.java
index 91f01b1d00..928a4d8f46 100644
--- 
a/endorsed/src/org.apache.sis.storage.xml/main/org/apache/sis/storage/gpx/StoreProvider.java
+++ 
b/endorsed/src/org.apache.sis.storage.xml/main/org/apache/sis/storage/gpx/StoreProvider.java
@@ -70,6 +70,19 @@ public final class StoreProvider extends 
StaxDataStoreProvider {
      */
     private static final Range<Version> VERSIONS = new Range<>(Version.class, 
V1_0, true, V1_1, true);
 
+    /**
+     * The default factory instance.
+     */
+    private static final StoreProvider INSTANCE = new StoreProvider();
+
+    /**
+     * {@return the default provider instance}.
+     * This method is invoked by Java service loader.
+     */
+    public static StoreProvider provider() {
+        return INSTANCE;
+    }
+
     /**
      * Creates a new GPX store provider.
      */
diff --git 
a/endorsed/src/org.apache.sis.storage.xml/test/org/apache/sis/storage/gpx/ReaderTest.java
 
b/endorsed/src/org.apache.sis.storage.xml/test/org/apache/sis/storage/gpx/ReaderTest.java
index 3886890ddf..79c3efac95 100644
--- 
a/endorsed/src/org.apache.sis.storage.xml/test/org/apache/sis/storage/gpx/ReaderTest.java
+++ 
b/endorsed/src/org.apache.sis.storage.xml/test/org/apache/sis/storage/gpx/ReaderTest.java
@@ -32,8 +32,6 @@ import org.apache.sis.storage.DataStoreException;
 import org.apache.sis.storage.gps.Fix;
 
 // Test dependencies
-import org.junit.jupiter.api.BeforeAll;
-import org.junit.jupiter.api.AfterAll;
 import org.junit.jupiter.api.Test;
 import static org.junit.jupiter.api.Assertions.*;
 import org.apache.sis.test.TestCase;
@@ -56,28 +54,13 @@ public final class ReaderTest extends TestCase {
     /**
      * The provider shared by all data stores created in this test class.
      */
-    private static StoreProvider provider;
+    private final StoreProvider provider;
 
     /**
      * Creates the provider to be shared by all data stores created in this 
test class.
      */
-    @BeforeAll
-    public static void createProvider() {
-        provider = new StoreProvider();
-    }
-
-    /**
-     * Disposes the data store provider after all tests have been completed.
-     */
-    @AfterAll
-    public static void disposeProvider() {
-        provider = null;
-    }
-
-    /**
-     * Creates a new test case.
-     */
     public ReaderTest() {
+        provider = StoreProvider.provider();
     }
 
     /**
@@ -86,7 +69,7 @@ public final class ReaderTest extends TestCase {
      * @param  version   identifies the version of the schema to test.
      * @param  resource  name of the test file.
      */
-    private static Store create(final TestData version, final String resource) 
throws DataStoreException {
+    private Store create(final TestData version, final String resource) throws 
DataStoreException {
         final var connector = new 
StorageConnector(version.openStream(resource));
         connector.setOption(OptionKey.GEOMETRY_LIBRARY, GeometryLibrary.ESRI);
         return new Store(provider, connector);
@@ -563,7 +546,7 @@ public final class ReaderTest extends TestCase {
      * Creates a data store for the {@code "1.1/route.xml"} test files using 
its URL instead of the input stream.
      * Using the URL makes easier for the data store to read the same data 
more than once.
      */
-    private static Store createFromURL() throws DataStoreException {
+    private Store createFromURL() throws DataStoreException {
         final StorageConnector connector = new 
StorageConnector(TestData.V1_1.getURL(TestData.ROUTE));
         connector.setOption(OptionKey.GEOMETRY_LIBRARY, GeometryLibrary.ESRI);
         connector.setOption(OptionKey.URL_ENCODING, "UTF-8");
diff --git 
a/endorsed/src/org.apache.sis.storage.xml/test/org/apache/sis/storage/gpx/UpdaterTest.java
 
b/endorsed/src/org.apache.sis.storage.xml/test/org/apache/sis/storage/gpx/UpdaterTest.java
index 6fdfbcd816..02ee8f6968 100644
--- 
a/endorsed/src/org.apache.sis.storage.xml/test/org/apache/sis/storage/gpx/UpdaterTest.java
+++ 
b/endorsed/src/org.apache.sis.storage.xml/test/org/apache/sis/storage/gpx/UpdaterTest.java
@@ -31,8 +31,6 @@ import org.apache.sis.storage.DataStoreException;
 import org.apache.sis.storage.StorageConnector;
 
 // Test dependencies
-import org.junit.jupiter.api.BeforeAll;
-import org.junit.jupiter.api.AfterAll;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.Test;
@@ -55,23 +53,7 @@ public final class UpdaterTest extends TestCase {
     /**
      * The provider shared by all data stores created in this test class.
      */
-    private static StoreProvider provider;
-
-    /**
-     * Creates the provider to be shared by all data stores created in this 
test class.
-     */
-    @BeforeAll
-    public static void createProvider() {
-        provider = new StoreProvider();
-    }
-
-    /**
-     * Disposes the data store provider after all tests have been completed.
-     */
-    @AfterAll
-    public static void disposeProvider() {
-        provider = null;
-    }
+    private final StoreProvider provider;
 
     /**
      * Temporary file where to write the GPX file.
@@ -79,9 +61,10 @@ public final class UpdaterTest extends TestCase {
     private Path file;
 
     /**
-     * Creates a new test case.
+     * Creates the provider to be shared by all data stores created in this 
test class.
      */
     public UpdaterTest() {
+        provider = StoreProvider.provider();
     }
 
     /**
diff --git 
a/endorsed/src/org.apache.sis.storage.xml/test/org/apache/sis/storage/gpx/WriterTest.java
 
b/endorsed/src/org.apache.sis.storage.xml/test/org/apache/sis/storage/gpx/WriterTest.java
index ba34700551..0f3c9acafb 100644
--- 
a/endorsed/src/org.apache.sis.storage.xml/test/org/apache/sis/storage/gpx/WriterTest.java
+++ 
b/endorsed/src/org.apache.sis.storage.xml/test/org/apache/sis/storage/gpx/WriterTest.java
@@ -30,8 +30,6 @@ import org.apache.sis.storage.StorageConnector;
 import org.apache.sis.storage.gps.Fix;
 
 // Test dependencies
-import org.junit.jupiter.api.BeforeAll;
-import org.junit.jupiter.api.AfterAll;
 import org.junit.jupiter.api.Test;
 import static org.junit.jupiter.api.Assertions.*;
 import org.apache.sis.test.TestUtilities;
@@ -54,23 +52,7 @@ public final class WriterTest extends TestCase {
     /**
      * The provider shared by all data stores created in this test class.
      */
-    private static StoreProvider provider;
-
-    /**
-     * Creates the provider to be shared by all data stores created in this 
test class.
-     */
-    @BeforeAll
-    public static void createProvider() {
-        provider = new StoreProvider();
-    }
-
-    /**
-     * Disposes the data store provider after all tests have been completed.
-     */
-    @AfterAll
-    public static void disposeProvider() {
-        provider = null;
-    }
+    private final StoreProvider provider;
 
     /**
      * Where to write the GPX file.
@@ -81,6 +63,7 @@ public final class WriterTest extends TestCase {
      * Creates a new test case.
      */
     public WriterTest() {
+        provider = StoreProvider.provider();
         output = new ByteArrayOutputStream();
     }
 
diff --git 
a/endorsed/src/org.apache.sis.storage/test/org/apache/sis/storage/esri/BILConsistencyTest.java
 
b/endorsed/src/org.apache.sis.storage/test/org/apache/sis/storage/esri/BILConsistencyTest.java
index fdf4d91f27..81149dcb9b 100644
--- 
a/endorsed/src/org.apache.sis.storage/test/org/apache/sis/storage/esri/BILConsistencyTest.java
+++ 
b/endorsed/src/org.apache.sis.storage/test/org/apache/sis/storage/esri/BILConsistencyTest.java
@@ -18,12 +18,12 @@ package org.apache.sis.storage.esri;
 
 import java.net.URL;
 import java.io.IOException;
+import org.apache.sis.util.Workaround;
 import org.apache.sis.storage.DataStoreException;
+import org.apache.sis.storage.GridCoverageResource;
 import org.apache.sis.storage.StorageConnector;
 
 // Test dependencies
-import org.junit.jupiter.api.AfterAll;
-import org.junit.jupiter.api.BeforeAll;
 import static org.junit.jupiter.api.Assertions.*;
 import org.apache.sis.storage.test.CoverageReadConsistency;
 
@@ -33,45 +33,34 @@ import org.apache.sis.storage.test.CoverageReadConsistency;
  *
  * @author  Martin Desruisseaux (Geomatys)
  */
-public final class BILConsistencyTest extends CoverageReadConsistency {
-    /**
-     * The store used for the test, opened only once.
-     */
-    private static RawRasterStore store;
-
+public final class BILConsistencyTest extends 
CoverageReadConsistency<RawRasterStore> {
     /**
      * Opens the test file to be used for all tests.
      *
      * @throws IOException if an error occurred while opening the file.
      * @throws DataStoreException if an error occurred while reading the file.
      */
-    @BeforeAll
-    public static void openFile() throws IOException, DataStoreException {
-        final URL url = BIPConsistencyTest.class.getResource("BIL.raw");
-        assertNotNull(url, "Test file not found.");
-        store = new RawRasterStore(null, new StorageConnector(url));
+    public BILConsistencyTest() throws IOException, DataStoreException {
+        super(openFile());
     }
 
     /**
-     * Closes the test file used by all tests.
-     *
-     * @throws DataStoreException if an error occurred while closing the file.
+     * Work around for RFE #4093999 in Sun's bug database
+     * ("Relax constraint on placement of this()/super() call in 
constructors").
      */
-    @AfterAll
-    public static void closeFile() throws DataStoreException {
-        final RawRasterStore s = store;
-        if (s != null) {
-            store = null;       // Clear first in case of failure.
-            s.close();
-        }
+    @Workaround(library="JDK", version="1.7")
+    private static RawRasterStore openFile() throws IOException, 
DataStoreException {
+        final URL url = BIPConsistencyTest.class.getResource("BIL.raw");
+        assertNotNull(url, "Test file not found.");
+        return new RawRasterStore(null, new StorageConnector(url));
     }
 
     /**
-     * Creates a new test case.
-     *
-     * @throws DataStoreException if an error occurred while fetching the 
first image.
+     * Work around for RFE #4093999 in Sun's bug database
+     * ("Relax constraint on placement of this()/super() call in 
constructors").
      */
-    public BILConsistencyTest() throws DataStoreException {
-        super(store);
+    @Override
+    protected GridCoverageResource resource() throws DataStoreException {
+        return store;
     }
 }
diff --git 
a/endorsed/src/org.apache.sis.storage/test/org/apache/sis/storage/esri/BIPConsistencyTest.java
 
b/endorsed/src/org.apache.sis.storage/test/org/apache/sis/storage/esri/BIPConsistencyTest.java
index 0b60d70c33..445d1006f6 100644
--- 
a/endorsed/src/org.apache.sis.storage/test/org/apache/sis/storage/esri/BIPConsistencyTest.java
+++ 
b/endorsed/src/org.apache.sis.storage/test/org/apache/sis/storage/esri/BIPConsistencyTest.java
@@ -18,12 +18,12 @@ package org.apache.sis.storage.esri;
 
 import java.net.URL;
 import java.io.IOException;
+import org.apache.sis.util.Workaround;
 import org.apache.sis.storage.DataStoreException;
+import org.apache.sis.storage.GridCoverageResource;
 import org.apache.sis.storage.StorageConnector;
 
 // Test dependencies
-import org.junit.jupiter.api.AfterAll;
-import org.junit.jupiter.api.BeforeAll;
 import static org.junit.jupiter.api.Assertions.*;
 import org.apache.sis.storage.test.CoverageReadConsistency;
 
@@ -33,45 +33,34 @@ import org.apache.sis.storage.test.CoverageReadConsistency;
  *
  * @author  Martin Desruisseaux (Geomatys)
  */
-public final class BIPConsistencyTest extends CoverageReadConsistency {
-    /**
-     * The store used for the test, opened only once.
-     */
-    private static RawRasterStore store;
-
+public final class BIPConsistencyTest extends 
CoverageReadConsistency<RawRasterStore> {
     /**
      * Opens the test file to be used for all tests.
      *
      * @throws IOException if an error occurred while opening the file.
      * @throws DataStoreException if an error occurred while reading the file.
      */
-    @BeforeAll
-    public static void openFile() throws IOException, DataStoreException {
-        final URL url = BIPConsistencyTest.class.getResource("BIP.raw");
-        assertNotNull(url, "Test file not found.");
-        store = new RawRasterStore(null, new StorageConnector(url));
+    public BIPConsistencyTest() throws IOException, DataStoreException {
+        super(openFile());
     }
 
     /**
-     * Closes the test file used by all tests.
-     *
-     * @throws DataStoreException if an error occurred while closing the file.
+     * Work around for RFE #4093999 in Sun's bug database
+     * ("Relax constraint on placement of this()/super() call in 
constructors").
      */
-    @AfterAll
-    public static void closeFile() throws DataStoreException {
-        final RawRasterStore s = store;
-        if (s != null) {
-            store = null;       // Clear first in case of failure.
-            s.close();
-        }
+    @Workaround(library="JDK", version="1.7")
+    private static RawRasterStore openFile() throws IOException, 
DataStoreException {
+        final URL url = BIPConsistencyTest.class.getResource("BIP.raw");
+        assertNotNull(url, "Test file not found.");
+        return new RawRasterStore(null, new StorageConnector(url));
     }
 
     /**
-     * Creates a new test case.
-     *
-     * @throws DataStoreException if an error occurred while fetching the 
first image.
+     * Work around for RFE #4093999 in Sun's bug database
+     * ("Relax constraint on placement of this()/super() call in 
constructors").
      */
-    public BIPConsistencyTest() throws DataStoreException {
-        super(store);
+    @Override
+    protected GridCoverageResource resource() throws DataStoreException {
+        return store;
     }
 }
diff --git 
a/endorsed/src/org.apache.sis.storage/test/org/apache/sis/storage/esri/BSQConsistencyTest.java
 
b/endorsed/src/org.apache.sis.storage/test/org/apache/sis/storage/esri/BSQConsistencyTest.java
index 66129a655e..52049a7eff 100644
--- 
a/endorsed/src/org.apache.sis.storage/test/org/apache/sis/storage/esri/BSQConsistencyTest.java
+++ 
b/endorsed/src/org.apache.sis.storage/test/org/apache/sis/storage/esri/BSQConsistencyTest.java
@@ -18,12 +18,12 @@ package org.apache.sis.storage.esri;
 
 import java.net.URL;
 import java.io.IOException;
+import org.apache.sis.util.Workaround;
 import org.apache.sis.storage.DataStoreException;
+import org.apache.sis.storage.GridCoverageResource;
 import org.apache.sis.storage.StorageConnector;
 
 // Test dependencies
-import org.junit.jupiter.api.AfterAll;
-import org.junit.jupiter.api.BeforeAll;
 import static org.junit.jupiter.api.Assertions.*;
 import org.apache.sis.storage.test.CoverageReadConsistency;
 
@@ -33,45 +33,34 @@ import org.apache.sis.storage.test.CoverageReadConsistency;
  *
  * @author  Martin Desruisseaux (Geomatys)
  */
-public final class BSQConsistencyTest extends CoverageReadConsistency {
-    /**
-     * The store used for the test, opened only once.
-     */
-    private static RawRasterStore store;
-
+public final class BSQConsistencyTest extends 
CoverageReadConsistency<RawRasterStore> {
     /**
      * Opens the test file to be used for all tests.
      *
      * @throws IOException if an error occurred while opening the file.
      * @throws DataStoreException if an error occurred while reading the file.
      */
-    @BeforeAll
-    public static void openFile() throws IOException, DataStoreException {
-        final URL url = BSQConsistencyTest.class.getResource("BSQ.raw");
-        assertNotNull(url, "Test file not found.");
-        store = new RawRasterStore(null, new StorageConnector(url));
+    public BSQConsistencyTest() throws IOException, DataStoreException {
+        super(openFile());
     }
 
     /**
-     * Closes the test file used by all tests.
-     *
-     * @throws DataStoreException if an error occurred while closing the file.
+     * Work around for RFE #4093999 in Sun's bug database
+     * ("Relax constraint on placement of this()/super() call in 
constructors").
      */
-    @AfterAll
-    public static void closeFile() throws DataStoreException {
-        final RawRasterStore s = store;
-        if (s != null) {
-            store = null;       // Clear first in case of failure.
-            s.close();
-        }
+    @Workaround(library="JDK", version="1.7")
+    private static RawRasterStore openFile() throws IOException, 
DataStoreException {
+        final URL url = BSQConsistencyTest.class.getResource("BSQ.raw");
+        assertNotNull(url, "Test file not found.");
+        return new RawRasterStore(null, new StorageConnector(url));
     }
 
     /**
-     * Creates a new test case.
-     *
-     * @throws DataStoreException if an error occurred while fetching the 
first image.
+     * Work around for RFE #4093999 in Sun's bug database
+     * ("Relax constraint on placement of this()/super() call in 
constructors").
      */
-    public BSQConsistencyTest() throws DataStoreException {
-        super(store);
+    @Override
+    protected GridCoverageResource resource() throws DataStoreException {
+        return store;
     }
 }
diff --git 
a/endorsed/src/org.apache.sis.storage/test/org/apache/sis/storage/image/SelfConsistencyTest.java
 
b/endorsed/src/org.apache.sis.storage/test/org/apache/sis/storage/image/SelfConsistencyTest.java
index 519f8e9694..a96c7020b3 100644
--- 
a/endorsed/src/org.apache.sis.storage/test/org/apache/sis/storage/image/SelfConsistencyTest.java
+++ 
b/endorsed/src/org.apache.sis.storage/test/org/apache/sis/storage/image/SelfConsistencyTest.java
@@ -18,12 +18,12 @@ package org.apache.sis.storage.image;
 
 import java.net.URL;
 import java.io.IOException;
+import org.apache.sis.util.Workaround;
 import org.apache.sis.storage.DataStoreException;
+import org.apache.sis.storage.GridCoverageResource;
 import org.apache.sis.storage.StorageConnector;
 
 // Test dependencies
-import org.junit.jupiter.api.AfterAll;
-import org.junit.jupiter.api.BeforeAll;
 import static org.junit.jupiter.api.Assertions.*;
 import org.apache.sis.storage.test.CoverageReadConsistency;
 
@@ -37,45 +37,34 @@ import org.apache.sis.storage.test.CoverageReadConsistency;
  *
  * @author  Martin Desruisseaux (Geomatys)
  */
-public final class SelfConsistencyTest extends CoverageReadConsistency {
-    /**
-     * The store used for the test, opened only once.
-     */
-    private static WorldFileStore store;
-
+public final class SelfConsistencyTest extends 
CoverageReadConsistency<WorldFileStore> {
     /**
      * Opens the test file to be used for all tests.
      *
      * @throws IOException if an error occurred while opening the file.
      * @throws DataStoreException if an error occurred while reading the file.
      */
-    @BeforeAll
-    public static void openFile() throws IOException, DataStoreException {
-        final URL url = WorldFileStoreTest.class.getResource("gradient.png");
-        assertNotNull(url, "Test file not found.");
-        store = new WorldFileStore(null, new StorageConnector(url));
+    public SelfConsistencyTest() throws IOException, DataStoreException {
+        super(openFile());
     }
 
     /**
-     * Closes the test file used by all tests.
-     *
-     * @throws DataStoreException if an error occurred while closing the file.
+     * Work around for RFE #4093999 in Sun's bug database
+     * ("Relax constraint on placement of this()/super() call in 
constructors").
      */
-    @AfterAll
-    public static void closeFile() throws DataStoreException {
-        final WorldFileStore s = store;
-        if (s != null) {
-            store = null;       // Clear first in case of failure.
-            s.close();
-        }
+    @Workaround(library="JDK", version="1.7")
+    private static WorldFileStore openFile() throws IOException, 
DataStoreException {
+        final URL url = WorldFileStoreTest.class.getResource("gradient.png");
+        assertNotNull(url, "Test file not found.");
+        return new WorldFileStore(null, new StorageConnector(url));
     }
 
     /**
-     * Creates a new test case.
-     *
-     * @throws DataStoreException if an error occurred while fetching the 
first image.
+     * Work around for RFE #4093999 in Sun's bug database
+     * ("Relax constraint on placement of this()/super() call in 
constructors").
      */
-    public SelfConsistencyTest() throws DataStoreException {
-        super(store.components().iterator().next());
+    @Override
+    protected GridCoverageResource resource() throws DataStoreException {
+        return store.components().iterator().next();
     }
 }
diff --git 
a/endorsed/src/org.apache.sis.storage/test/org/apache/sis/storage/test/CoverageReadConsistency.java
 
b/endorsed/src/org.apache.sis.storage/test/org/apache/sis/storage/test/CoverageReadConsistency.java
index 0a7c41cc97..93fb274bc1 100644
--- 
a/endorsed/src/org.apache.sis.storage/test/org/apache/sis/storage/test/CoverageReadConsistency.java
+++ 
b/endorsed/src/org.apache.sis.storage/test/org/apache/sis/storage/test/CoverageReadConsistency.java
@@ -27,9 +27,11 @@ import org.apache.sis.coverage.grid.GridCoverage;
 import org.apache.sis.coverage.grid.GridDerivation;
 import org.apache.sis.coverage.grid.GridGeometry;
 import org.apache.sis.coverage.grid.GridExtent;
+import org.apache.sis.storage.DataStore;
 import org.apache.sis.storage.DataStoreException;
 import org.apache.sis.storage.GridCoverageResource;
 import org.apache.sis.storage.RasterLoadingStrategy;
+import org.apache.sis.util.Workaround;
 import org.apache.sis.util.ArraysExt;
 import org.apache.sis.util.internal.StandardDateFormat;
 import org.apache.sis.util.internal.Numerics;
@@ -40,6 +42,7 @@ import org.apache.sis.math.StatisticsFormat;
 // Test dependencies
 import org.junit.jupiter.api.AfterAll;
 import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestInstance;
 import static org.junit.jupiter.api.Assertions.*;
 import org.apache.sis.test.TestUtilities;
 import org.apache.sis.test.TestCase;
@@ -57,19 +60,28 @@ import org.apache.sis.test.TestCase;
  * we consider that the code reading the full extent is usually simpler than 
the code reading
  * a subset of data.
  *
- * <p>This class is not thread-safe. Only one instance should exist in the JVM 
at a given time
- * (because of the use of static fields).</p>
+ * <p>This class is not thread-safe. However, instances of this class can be 
reused for many test methods.</p>
+ *
+ * @param  <S> the data store class to test.
  *
  * @author  Martin Desruisseaux (Geomatys)
  */
-public class CoverageReadConsistency extends TestCase {
+@TestInstance(TestInstance.Lifecycle.PER_CLASS)
+public abstract class CoverageReadConsistency<S extends DataStore> extends 
TestCase {
     /**
      * A constant for identifying the codes working on two dimensional slices.
      */
     private static final int BIDIMENSIONAL = 2;
 
     /**
-     * The resource to test.
+     * The data store to test. It will be closed after all tests finished.
+     *
+     * @see #closeFile()
+     */
+    protected final S store;
+
+    /**
+     * The resource to test. May be the same instance as {@link #store}.
      */
     private final GridCoverageResource resource;
 
@@ -117,19 +129,20 @@ public class CoverageReadConsistency extends TestCase {
      * Statistics about execution time.
      * Created only in benchmark mode.
      */
-    private static List<Statistics> statistics;
+    private List<Statistics> statistics;
 
     /**
      * Creates a new tester. This constructor reads immediately the coverage 
at full extent and full resolution.
      * That full coverage will be used as a reference for verifying the pixel 
values read in sub-domains.
      * Any mismatch in pixel values will cause immediate test failure.
      *
-     * @param  tested  the resource to test.
+     * @param  store  the data store to test.
      * @throws DataStoreException if the full coverage cannot be read.
      */
-    public CoverageReadConsistency(final GridCoverageResource tested) throws 
DataStoreException {
-        resource       = tested;
-        full           = tested.read(null, null);
+    public CoverageReadConsistency(final S store) throws DataStoreException {
+        this.store     = store;
+        resource       = resource();
+        full           = resource.read(null, null);
         random         = TestUtilities.createRandomNumberGenerator();
         numIterations  = 100;
         failOnMismatch = true;
@@ -140,14 +153,16 @@ public class CoverageReadConsistency extends TestCase {
      * This tester may be used for benchmarking instead of JUnit tests.
      * Mismatched pixel values will be reported in statistics instead of 
causing test failure.
      *
+     * @param  store      the data store to close after all tests are 
completed.
      * @param  tested     the resource to test.
      * @param  reference  full coverage read from the {@code resource}, or 
{@code null} if none.
      * @param  seed       seed for random number generator. Used for 
reproducible "random" values.
      * @param  readCount  number of read operations to perform.
      */
-    public CoverageReadConsistency(final GridCoverageResource tested, final 
GridCoverage reference,
-                                   final long seed, final int readCount)
+    public CoverageReadConsistency(final S store, final GridCoverageResource 
tested,
+                final GridCoverage reference, final long seed, final int 
readCount)
     {
+        this.store     = store;
         resource       = tested;
         full           = reference;
         random         = TestUtilities.createRandomNumberGenerator(seed);
@@ -155,6 +170,21 @@ public class CoverageReadConsistency extends TestCase {
         failOnMismatch = false;
     }
 
+    /**
+     * Returns the resource to read from the {@linkplain #store}.
+     * This method shall not use any other field and shall not invoke any 
other method,
+     * because this method is invoked during object construction.
+     *
+     * <p>This method is a work around for RFE #4093999 in Sun's bug database
+     * ("Relax constraint on placement of this()/super() call in constructors")
+     * and will be removed (replaced by constructor argument) in a future 
version.</p>
+     *
+     * @return the resource to test.
+     * @throws DataStoreException if an error occurred while reading the 
resource.
+     */
+    @Workaround(library="JDK", version="1.7")
+    protected abstract GridCoverageResource resource() throws 
DataStoreException;
+
     /**
      * Tests reading in random sub-regions starting at coordinates (0,0).
      * Data are read at full resolution (no-subsampling) and all bands are 
read.
@@ -164,6 +194,9 @@ public class CoverageReadConsistency extends TestCase {
      */
     @Test
     public void testSubRegionAtOrigin() throws DataStoreException {
+        allowOffsets     = false;
+        allowBandSubset  = false;
+        allowSubsampling = false;
         readAndCompareRandomRegions("Subregions at (0,0)");
     }
 
@@ -175,7 +208,9 @@ public class CoverageReadConsistency extends TestCase {
      */
     @Test
     public void testSubRegionsAnywhere() throws DataStoreException {
-        allowOffsets = true;
+        allowOffsets     = true;
+        allowBandSubset  = false;
+        allowSubsampling = false;
         readAndCompareRandomRegions("Subregions");
     }
 
@@ -187,6 +222,8 @@ public class CoverageReadConsistency extends TestCase {
      */
     @Test
     public void testSubsamplingAtOrigin() throws DataStoreException {
+        allowOffsets     = false;
+        allowBandSubset  = false;
         allowSubsampling = true;
         readAndCompareRandomRegions("Subsampling at (0,0)");
     }
@@ -200,6 +237,7 @@ public class CoverageReadConsistency extends TestCase {
     @Test
     public void testSubsamplingAnywhere() throws DataStoreException {
         allowOffsets     = true;
+        allowBandSubset  = false;
         allowSubsampling = true;
         readAndCompareRandomRegions("Subsampling");
     }
@@ -211,7 +249,9 @@ public class CoverageReadConsistency extends TestCase {
      */
     @Test
     public void testBandSubsetAtOrigin() throws DataStoreException {
-        allowBandSubset = true;
+        allowOffsets     = false;
+        allowBandSubset  = true;
+        allowSubsampling = false;
         readAndCompareRandomRegions("Bands at (0,0)");
     }
 
@@ -222,8 +262,9 @@ public class CoverageReadConsistency extends TestCase {
      */
     @Test
     public void testBandSubsetAnywhere() throws DataStoreException {
-        allowOffsets    = true;
-        allowBandSubset = true;
+        allowOffsets     = true;
+        allowBandSubset  = true;
+        allowSubsampling = false;
         readAndCompareRandomRegions("Bands");
     }
 
@@ -435,7 +476,7 @@ nextSlice:  for (;;) {
      */
     @AfterAll
     @SuppressWarnings("UseOfSystemOutOrSystemErr")
-    public static void printDurations() {
+    public void printDurations() {
         if (statistics != null) {
             // It is too late for using `TestCase.out`.
             
System.out.print(StatisticsFormat.getInstance().format(statistics.toArray(Statistics[]::new)));
@@ -542,4 +583,14 @@ nextSlice:  for (;;) {
             }
         }
     }
+
+    /**
+     * Closes the resource used by all tests.
+     *
+     * @throws DataStoreException if an error occurred while closing the 
resource.
+     */
+    @AfterAll
+    public void closeFile() throws DataStoreException {
+        store.close();
+    }
 }
diff --git 
a/endorsed/src/org.apache.sis.util/test/org/apache/sis/test/FailureDetailsReporter.java
 
b/endorsed/src/org.apache.sis.util/test/org/apache/sis/test/FailureDetailsReporter.java
index ce3cb0008e..e4452c1820 100644
--- 
a/endorsed/src/org.apache.sis.util/test/org/apache/sis/test/FailureDetailsReporter.java
+++ 
b/endorsed/src/org.apache.sis.util/test/org/apache/sis/test/FailureDetailsReporter.java
@@ -65,8 +65,8 @@ public final class FailureDetailsReporter implements 
BeforeEachCallback, AfterEa
         LogRecordCollector.INSTANCE.setCurrentTest(null);
         if (description.getExecutionException().isPresent()) {
             description.getTestMethod().ifPresent((methodName) -> {
-                final long seed = TestCase.randomSeed;
-                if (seed != 0) {
+                final Long seed = TestUtilities.randomSeed.get();
+                if (seed != null) {
                     final PrintWriter out = TestCase.out;
                     out.print("Random number generator for ");
                     
out.print(description.getTestClass().map(Class::getCanonicalName).orElse("<?>"));
@@ -79,7 +79,7 @@ public final class FailureDetailsReporter implements 
BeforeEachCallback, AfterEa
                 TestCase.flushOutput();
             });
         }
-        TestCase.randomSeed = 0;
+        TestUtilities.randomSeed.remove();
     }
 
     /**
diff --git 
a/endorsed/src/org.apache.sis.util/test/org/apache/sis/test/TestCase.java 
b/endorsed/src/org.apache.sis.util/test/org/apache/sis/test/TestCase.java
index b3a225f2e5..4bd6e7c8b7 100644
--- a/endorsed/src/org.apache.sis.util/test/org/apache/sis/test/TestCase.java
+++ b/endorsed/src/org.apache.sis.util/test/org/apache/sis/test/TestCase.java
@@ -24,6 +24,7 @@ import java.io.UnsupportedEncodingException;
 import org.apache.sis.util.logging.MonolineFormatter;
 
 // Test dependencies
+import org.junit.jupiter.api.TestInstance;
 import org.junit.jupiter.api.extension.ExtendWith;
 
 
@@ -47,6 +48,7 @@ import org.junit.jupiter.api.extension.ExtendWith;
  * @author  Martin Desruisseaux (Geomatys)
  */
 @ExtendWith(FailureDetailsReporter.class)
+@TestInstance(TestInstance.Lifecycle.PER_METHOD)
 public abstract class TestCase {
     /**
      * A flag for code that are pending future SIS development before to be 
enabled.
@@ -72,13 +74,6 @@ public abstract class TestCase {
      */
     public static final double STRICT = 0;
 
-    /**
-     * The seed for the random number generator created by {@link 
TestUtilities#createRandomNumberGenerator()},
-     * or 0 if none. This information is used for printing the seed in case of 
test failure, in order to allow
-     * the developer to reproduce the failure.
-     */
-    static long randomSeed;
-
     /**
      * The output writer where to print debugging information (never {@code 
null}).
      * Texts sent to this printer will be shown only if the test fails, or if 
the
diff --git 
a/endorsed/src/org.apache.sis.util/test/org/apache/sis/test/TestUtilities.java 
b/endorsed/src/org.apache.sis.util/test/org/apache/sis/test/TestUtilities.java
index eb19f0b6f5..b929a5181e 100644
--- 
a/endorsed/src/org.apache.sis.util/test/org/apache/sis/test/TestUtilities.java
+++ 
b/endorsed/src/org.apache.sis.util/test/org/apache/sis/test/TestUtilities.java
@@ -92,6 +92,13 @@ public final class TestUtilities extends Static {
      */
     public static final ThreadGroup THREADS = new ThreadGroup("SIS-Tests");
 
+    /**
+     * The seed for the random number generator created by {@link 
#createRandomNumberGenerator()}, or null if none.
+     * This information is used for printing the seed in case of test failure, 
in order to allow the developer to
+     * reproduce the failure.
+     */
+    static final ThreadLocal<Long> randomSeed = new ThreadLocal<>();
+
     /**
      * Do not allow instantiation of this class.
      */
@@ -170,10 +177,8 @@ public final class TestUtilities extends Static {
      * @return a new random number generator initialized with a random seed.
      */
     public static Random createRandomNumberGenerator() {
-        long seed;
-        do seed = StrictMath.round(StrictMath.random() * (1L << 48));
-        while (seed == 0); // 0 is a sentinel value for "no generator".
-        TestCase.randomSeed = seed;
+        final long seed = StrictMath.round(StrictMath.random() * (1L << 48));
+        randomSeed.set(seed);
         return new Random(seed);
     }
 
@@ -196,7 +201,7 @@ public final class TestUtilities extends Static {
      */
     @Debug
     public static Random createRandomNumberGenerator(final long seed) {
-        TestCase.randomSeed = seed;
+        randomSeed.set(seed);
         return new Random(seed);
     }
 
diff --git 
a/incubator/src/org.apache.sis.storage.coveragejson/test/org/apache/sis/storage/coveragejson/binding/BindingTest.java
 
b/incubator/src/org.apache.sis.storage.coveragejson/test/org/apache/sis/storage/coveragejson/binding/BindingTest.java
index 34497f0da7..46bb1ff72c 100644
--- 
a/incubator/src/org.apache.sis.storage.coveragejson/test/org/apache/sis/storage/coveragejson/binding/BindingTest.java
+++ 
b/incubator/src/org.apache.sis.storage.coveragejson/test/org/apache/sis/storage/coveragejson/binding/BindingTest.java
@@ -30,13 +30,12 @@ import java.util.Map;
 import jakarta.json.JsonObject;
 import jakarta.json.bind.Jsonb;
 import jakarta.json.bind.JsonbBuilder;
-import jakarta.json.bind.JsonbConfig;
 import org.eclipse.yasson.YassonConfig;
 
 // Test dependencies
 import org.junit.jupiter.api.AfterAll;
-import org.junit.jupiter.api.BeforeAll;
 import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestInstance;
 import static org.junit.jupiter.api.Assertions.*;
 
 
@@ -45,13 +44,13 @@ import static org.junit.jupiter.api.Assertions.*;
  *
  * @author Johann Sorel (Geomatys)
  */
+@TestInstance(TestInstance.Lifecycle.PER_CLASS)
 public class BindingTest {
 
-    private static final JsonbConfig CONFIG = new 
YassonConfig().withFormatting(true);
-
-    private static Jsonb jsonb;
+    private final Jsonb jsonb;
 
     public BindingTest() {
+        jsonb = JsonbBuilder.create(new YassonConfig().withFormatting(true));
     }
 
     public static String readResource(String path) throws IOException {
@@ -80,13 +79,8 @@ public class BindingTest {
         assertEquals(formattedJson, jsonb.toJson(candidate));
     }
 
-    @BeforeAll
-    public static void beforeClass() {
-        jsonb = JsonbBuilder.create(CONFIG);
-    }
-
     @AfterAll
-    public static void afterClass() throws Exception {
+    public void afterClass() throws Exception {
         jsonb.close();
     }
 


Reply via email to