You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@isis.apache.org by da...@apache.org on 2021/06/21 08:46:17 UTC

[isis] branch ISIS-2756 created (now b5ca02d)

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

danhaywood pushed a change to branch ISIS-2756
in repository https://gitbox.apache.org/repos/asf/isis.git.


      at b5ca02d  ISIS-2756: renames PublishedEndpoints so SoapPublishedEndpoints

This branch includes the following new commits:

     new 88e88c2  ISIS-2756: refactors PublishedEndpoints so reusable for junit 5
     new b5ca02d  ISIS-2756: renames PublishedEndpoints so SoapPublishedEndpoints

The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


[isis] 02/02: ISIS-2756: renames PublishedEndpoints so SoapPublishedEndpoints

Posted by da...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

danhaywood pushed a commit to branch ISIS-2756
in repository https://gitbox.apache.org/repos/asf/isis.git

commit b5ca02d96a65d20496638f2a3a691ddf0453629b
Author: danhaywood <da...@haywood-associates.co.uk>
AuthorDate: Mon Jun 21 09:45:42 2021 +0100

    ISIS-2756: renames PublishedEndpoints so SoapPublishedEndpoints
---
 ...dEndpoints.java => SoapPublishedEndpoints.java} | 28 +++++++++++-----------
 1 file changed, 14 insertions(+), 14 deletions(-)

diff --git a/testing/unittestsupport/applib/src/main/java/org/apache/isis/testing/unittestsupport/applib/core/soap/PublishedEndpoints.java b/testing/unittestsupport/applib/src/main/java/org/apache/isis/testing/unittestsupport/applib/core/soap/SoapPublishedEndpoints.java
similarity index 82%
rename from testing/unittestsupport/applib/src/main/java/org/apache/isis/testing/unittestsupport/applib/core/soap/PublishedEndpoints.java
rename to testing/unittestsupport/applib/src/main/java/org/apache/isis/testing/unittestsupport/applib/core/soap/SoapPublishedEndpoints.java
index 6695b40..85d5684 100644
--- a/testing/unittestsupport/applib/src/main/java/org/apache/isis/testing/unittestsupport/applib/core/soap/PublishedEndpoints.java
+++ b/testing/unittestsupport/applib/src/main/java/org/apache/isis/testing/unittestsupport/applib/core/soap/SoapPublishedEndpoints.java
@@ -39,7 +39,7 @@ import lombok.val;
  *     {@link #dispose() dispose}d of.
  * </p>
  */
-public class PublishedEndpoints {
+public class SoapPublishedEndpoints {
 
     /**
      * For any endpoints where the address is not specified, ports are assigned starting from this.
@@ -49,9 +49,9 @@ public class PublishedEndpoints {
     /**
      * Lazily instantiates the singleton, using the {@link #INITIAL_PORT_DEFAULT default initial port}.
      */
-    public static PublishedEndpoints instance() {
+    public static SoapPublishedEndpoints instance() {
         if(instance == null) {
-            return new PublishedEndpoints();
+            return new SoapPublishedEndpoints();
         }
         return instance;
     }
@@ -63,14 +63,14 @@ public class PublishedEndpoints {
      *     If called again with a different port, then will {@link #dispose() discard} the singleton and start over.
      * </p>
      */
-    public static PublishedEndpoints instance(final int initialPort) {
+    public static SoapPublishedEndpoints instance(final int initialPort) {
         if (instance != null) {
             if (instance.port != initialPort) {
                 dispose();
             }
         }
         if (instance == null) {
-            instance = new PublishedEndpoints(initialPort);
+            instance = new SoapPublishedEndpoints(initialPort);
         }
         return instance;
     }
@@ -82,12 +82,12 @@ public class PublishedEndpoints {
         instance = null;
     }
 
-    private static PublishedEndpoints instance;
+    private static SoapPublishedEndpoints instance;
 
-    PublishedEndpoints(){
+    SoapPublishedEndpoints(){
         this(INITIAL_PORT_DEFAULT);
     }
-    PublishedEndpoints(int initialPort){
+    SoapPublishedEndpoints(int initialPort){
         this.initialPort = initialPort;
         this.port = this.initialPort;
     }
@@ -96,37 +96,37 @@ public class PublishedEndpoints {
     private final Map<Class<?>, SoapEndpoint> soapEndpointByType = _Maps.newLinkedHashMap();
 
 
-    public PublishedEndpoints publishIfRequired(final Class<?> endpointClass, final String endpointAddress) {
+    public SoapPublishedEndpoints publishIfRequired(final Class<?> endpointClass, final String endpointAddress) {
         return publishIfRequired(new SoapEndpointSpec(endpointClass, endpointAddress));
     }
 
-    public PublishedEndpoints publishIfRequired(Class<?>... endpointClasses) {
+    public SoapPublishedEndpoints publishIfRequired(Class<?>... endpointClasses) {
         val soapEndpointSpecs = stream(endpointClasses)
                 .map(SoapEndpointSpec::asSoapEndpointSpec)
                 .collect(Collectors.toCollection(ArrayList::new));
         return publishIfRequired(soapEndpointSpecs);
     }
 
-    public PublishedEndpoints publishIfRequired(final List<Class<?>> endpointClasses) {
+    public SoapPublishedEndpoints publishIfRequired(final List<Class<?>> endpointClasses) {
         val soapEndpointSpecs = stream(endpointClasses)
                 .map(SoapEndpointSpec::asSoapEndpointSpec)
                 .collect(Collectors.toCollection(ArrayList::new));
         return publishIfRequired(soapEndpointSpecs);
     }
 
-    public PublishedEndpoints publishIfRequired(SoapEndpointSpec... soapEndpointSpecs) {
+    public SoapPublishedEndpoints publishIfRequired(SoapEndpointSpec... soapEndpointSpecs) {
         val soapEndpointSpecs2 = stream(soapEndpointSpecs)
                 .collect(Collectors.toCollection(ArrayList::new));
         return instance.publishIfRequired(soapEndpointSpecs2);
     }
 
-    public PublishedEndpoints publishIfRequired(final Iterable<SoapEndpointSpec> soapEndpointSpecs) {
+    public SoapPublishedEndpoints publishIfRequired(final Iterable<SoapEndpointSpec> soapEndpointSpecs) {
         val soapEndpointSpecs2 = stream(soapEndpointSpecs)
                 .collect(Collectors.toCollection(ArrayList::new));
         return instance.publishIfRequired(soapEndpointSpecs2);
     }
 
-    public PublishedEndpoints publishEndpointIfRequired(final List<SoapEndpointSpec> soapEndpointSpecs) {
+    public SoapPublishedEndpoints publishEndpointIfRequired(final List<SoapEndpointSpec> soapEndpointSpecs) {
         // merge in any new endpoints to static cache
         for (SoapEndpointSpec soapEndpointSpec : soapEndpointSpecs) {
             final Class<?> endpointClass = soapEndpointSpec.getEndpointClass();

[isis] 01/02: ISIS-2756: refactors PublishedEndpoints so reusable for junit 5

Posted by da...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

danhaywood pushed a commit to branch ISIS-2756
in repository https://gitbox.apache.org/repos/asf/isis.git

commit 88e88c232918f7c1fb2d394098fad817dbd63cbc
Author: danhaywood <da...@haywood-associates.co.uk>
AuthorDate: Mon Jun 21 09:45:02 2021 +0100

    ISIS-2756: refactors PublishedEndpoints so reusable for junit 5
---
 .../adoc/modules/unittestsupport/pages/about.adoc  | 125 ++++++++++++---------
 .../unittestsupport/applib/core/files/Files.java   |  41 ++-----
 .../applib/core/soap/PublishedEndpoints.java       | 106 ++++++++++++++++-
 .../core/soap/SoapEndpointPublishingRule.java      |  41 +++----
 4 files changed, 197 insertions(+), 116 deletions(-)

diff --git a/testing/unittestsupport/adoc/modules/unittestsupport/pages/about.adoc b/testing/unittestsupport/adoc/modules/unittestsupport/pages/about.adoc
index 05890a7..824d0fe 100644
--- a/testing/unittestsupport/adoc/modules/unittestsupport/pages/about.adoc
+++ b/testing/unittestsupport/adoc/modules/unittestsupport/pages/about.adoc
@@ -6,6 +6,44 @@ Apache Isis provides a number of helpers to help unit test your domain objects.
 
 
 
+== Maven Configuration
+
+TODO: move this earlier, standard format.
+
+Apache Isis' unit test support is most easily configured through a dependency on the `isis-mavendeps-testing` module:
+
+[source,xml]
+----
+<dependency>
+    <groupId>org.apache.isis.mavendeps</groupId>
+    <artifactId>isis-mavendeps-unittests</artifactId>
+    <scope>test</scope>                             <!--1-->
+    <type>pom</type>
+</dependency>
+----
+<1> Normally `test`; usual Maven scoping rules apply.
+
+This will set up unit testing support.
+There is no need to specify the version if you inherit from from the xref:docs:parent-pom:about.adoc[Parent POM].
+
+
+
+include::testing:ROOT:partial$surefire-configuration-caution.adoc[]
+
+If you just want to set up unit testing support, then use:
+
+[source,xml]
+----
+<dependency>
+    <groupId>org.apache.isis.core</groupId>
+    <artifactId>isis-core-unittestsupport</artifactId>
+    <scope>test</scope>
+</dependency>
+----
+
+
+
+
 == Contract Tests
 
 Contract tests ensure that all instances of a particular idiom/pattern that occur within your codebase are implemented correctly.
@@ -109,6 +147,11 @@ To see what's going on (and to identify any skipped relationships), use the `wit
 If any assertion fails then the error should be descriptive enough to figure out the problem (without enabling logging).
 
 
+=== Comparable
+
+TODO
+ComparableContractTest_compareTo
+
 
 === Value Objects
 
@@ -199,9 +242,6 @@ The example tests can be found https://github.com/apache/isis/tree/master/core/u
 
 == SOAP Fake Endpoints
 
-
-
-No man is an island, so the saying goes, and neither are most applications.
 Chances are that at some point you may need to integrate your Apache Isis application to other external systems, possibly using old-style SOAP web services.
 The SOAP client in this case could be a domain service within your app, or it might be externalized, eg invoked through a scheduler or using link:http://camel.apache.org[Apache Camel].
 
@@ -209,6 +249,8 @@ While you will want to (of course) perform manual system testing/UAT with a test
 
 The `SoapEndpointPublishingRule` is a simple JUnit rule that allows you to run a fake SOAP endpoint within an unit test.
 
+NOTE: although originally intended for use with JUnit4, it can also used in JUnit5 tests; details below.
+
 
 === `SoapEndpointPublishingRule`
 
@@ -233,17 +275,17 @@ Your unit test uses the rule by specifying the endpoint class (must have a no-ar
 public class FakeExternalSystemEndpointRuleTest {
     @Rule
     public SoapEndpointPublishingRule serverRule =
-        new SoapEndpointPublishingRule(FakeExternalSystemEndpoint.class);         // <1>
+        new SoapEndpointPublishingRule(FakeExternalSystemEndpoint.class);         // <.>
     private FakeExternalSystemEndpoint fakeServerEndpoint;
-    private DemoObject externalSystemContract;                                    // <2>
+    private DemoObject externalSystemContract;                                    // <.>
     @Before
     public void setUp() throws Exception {
         fakeServerEndpoint =
-            serverRule.getEndpointImplementor(FakeExternalSystemEndpoint.class);  // <3>
+            serverRule.getEndpointImplementor(FakeExternalSystemEndpoint.class);  // <.>
         final String endpointAddress =
-            serverRule.getEndpointAddress(FakeExternalSystemEndpoint.class);      // <4>
-        final DemoObjectService externalSystemService =                           // <5>
-                new DemoObjectService(ExternalSystemWsdl.getWsdl());              // <6>
+            serverRule.getEndpointAddress(FakeExternalSystemEndpoint.class);      // <.>
+        final DemoObjectService externalSystemService =                           // <.>
+                new DemoObjectService(ExternalSystemWsdl.getWsdl());              // <.>
         externalSystemContract = externalSystemService.getDemoObjectOverSOAP();
         BindingProvider provider = (BindingProvider) externalSystemContract;
         provider.getRequestContext().put(
@@ -254,31 +296,35 @@ public class FakeExternalSystemEndpointRuleTest {
     @Test
     public void happy_case() throws Exception {
         // given
-        final Update update = new Update();                              // <7>
+        final Update update = new Update();                              // <.>
         ...
+
         // expect
-        final UpdateResponse response = new UpdateResponse();            // <8>
+        final UpdateResponse response = new UpdateResponse();            // <.>
         ...
         fakeServerEndpoint.control().setResponse(updateResponse);
+
         // when
-        PostResponse response = externalSystemContract.post(update);     // <9>
+        PostResponse response = externalSystemContract.post(update);     // <.>
+
         // then
-        final List<Update> updates =                                     // <10>
+        final List<Update> updates =                                     // <.>
             fakeServerEndpoint.control().getUpdates();
         ...
     }
 }
 ----
-<1> specify the class that implements the endpoint (must have a no-arg constructor)
-<2> the SOAP contract as defined in WSDL and generated by wsdl2java
-<3> get hold of the fake server-side endpoint from the rule...
-<4> \... and its endpoint address
-<5> use factory (also generated by wsdl2java) to create client-side endpoint
-<6> `getWsdl()` is a utility method to return a URL for the WSDL (eg from the classpath)
-<7> create a request object in order to invoke the SOAP web service
-<8> instruct the fake server endpoint how to respond
-<9> invoke the web service
-<10> check the fake server endpoint was correctly invoked etc.
+
+<.> specify the class that implements the endpoint (must have a no-arg constructor)
+<.> the SOAP contract as defined in WSDL and generated by wsdl2java
+<.> get hold of the fake server-side endpoint from the rule...
+<.> \... and its endpoint address
+<.> use factory (also generated by wsdl2java) to create client-side endpoint
+<.> `getWsdl()` is a utility method to return a URL for the WSDL (eg from the classpath)
+<.> create a request object in order to invoke the SOAP web service
+<.> instruct the fake server endpoint how to respond
+<.> invoke the web service
+<.> check the fake server endpoint was correctly invoked etc.
 
 
 The rule can also host multiple endpoints; just provide multiple classes in the constructor:
@@ -314,36 +360,5 @@ These are useful if you have exampler XML-serialized representations of the SOAP
 
 
 
-== Maven Configuration
-
-Apache Isis' unit test support is most easily configured through a dependency on the `isis-mavendeps-testing` module:
-
-[source,xml]
-----
-<dependency>
-    <groupId>org.apache.isis.mavendeps</groupId>
-    <artifactId>isis-mavendeps-unittests</artifactId>
-    <scope>test</scope>                             <!--1-->
-    <type>pom</type>
-</dependency>
-----
-<1> Normally `test`; usual Maven scoping rules apply.
-
-This will set up unit testing support.
-There is no need to specify the version if you inherit from from the xref:docs:parent-pom:about.adoc[Parent POM].
-
-
-
-include::testing:ROOT:partial$surefire-configuration-caution.adoc[]
-
-If you just want to set up unit testing support, then use:
-
-[source,xml]
-----
-<dependency>
-    <groupId>org.apache.isis.core</groupId>
-    <artifactId>isis-core-unittestsupport</artifactId>
-    <scope>test</scope>
-</dependency>
-----
+== Files
 
diff --git a/testing/unittestsupport/applib/src/main/java/org/apache/isis/testing/unittestsupport/applib/core/files/Files.java b/testing/unittestsupport/applib/src/main/java/org/apache/isis/testing/unittestsupport/applib/core/files/Files.java
index d22eefd..cf9bf6b 100644
--- a/testing/unittestsupport/applib/src/main/java/org/apache/isis/testing/unittestsupport/applib/core/files/Files.java
+++ b/testing/unittestsupport/applib/src/main/java/org/apache/isis/testing/unittestsupport/applib/core/files/Files.java
@@ -21,6 +21,7 @@ package org.apache.isis.testing.unittestsupport.applib.core.files;
 import java.io.File;
 import java.io.FileFilter;
 import java.io.FilenameFilter;
+import java.util.Objects;
 
 import org.apache.isis.commons.internal.exceptions._Exceptions;
 
@@ -34,10 +35,6 @@ import lombok.experimental.UtilityClass;
 public final class Files {
 
 
-    // /////////////////////////////////////////////////////
-    // delete files
-    // /////////////////////////////////////////////////////
-
     public enum Recursion {
         DO_RECURSE, DONT_RECURSE
     }
@@ -48,7 +45,7 @@ public final class Files {
      *            directory to start deleting from
      * @param filePrefix
      *            file name prefix (no wild cards)
-     * @param recursion
+     * @param recursion - whether to recurse or not
      */
     public static void deleteFilesWithPrefix(final String directoryName, final String filePrefix, Recursion recursion) {
         deleteFiles(directoryName, filterFileNamePrefix(filePrefix), recursion);
@@ -84,25 +81,22 @@ public final class Files {
 
     static void deleteFiles(final File directory, final FilenameFilter filter, Recursion recursion, Deleter deleter) {
         try {
-            for (final File file : directory.listFiles(filter)) {
+            for (final File file : Objects.requireNonNull(directory.listFiles(filter))) {
                 deleter.deleteFile(file);
             }
-        } catch (NullPointerException e) {
+        } catch (NullPointerException ignored) {
         }
 
         if (recursion == Recursion.DO_RECURSE) {
             try {
-                for (final File subdir : directory.listFiles(filterDirectory())) {
+                for (final File subdir : Objects.requireNonNull(directory.listFiles(filterDirectory()))) {
                     deleteFiles(subdir, filter, recursion, deleter);
                 }
-            } catch (NullPointerException e) {
+            } catch (NullPointerException ignored) {
             }
         }
     }
 
-    // /////////////////////////////////////////////////////
-    // filters
-    // /////////////////////////////////////////////////////
 
     public static FilenameFilter and(final FilenameFilter... filters) {
         return new FilenameFilter() {
@@ -120,32 +114,15 @@ public final class Files {
     }
 
     public static FilenameFilter filterFileNameExtension(final String fileExtension) {
-        return new FilenameFilter() {
-
-            @Override
-            public boolean accept(final File arg0, final String arg1) {
-                return arg1.endsWith(fileExtension);
-            }
-        };
+        return (directory, fileName) -> fileName.endsWith(fileExtension);
     }
 
     public static FilenameFilter filterFileNamePrefix(final String filePrefix) {
-        return new FilenameFilter() {
-
-            @Override
-            public boolean accept(final File arg0, final String arg1) {
-                return arg1.startsWith(filePrefix);
-            }
-        };
+        return (directory, fileName) -> fileName.startsWith(filePrefix);
     }
 
     public static FileFilter filterDirectory() {
-        return new FileFilter() {
-            @Override
-            public boolean accept(File arg0) {
-                return arg0.isDirectory();
-            }
-        };
+        return File::isDirectory;
     }
 
 }
diff --git a/testing/unittestsupport/applib/src/main/java/org/apache/isis/testing/unittestsupport/applib/core/soap/PublishedEndpoints.java b/testing/unittestsupport/applib/src/main/java/org/apache/isis/testing/unittestsupport/applib/core/soap/PublishedEndpoints.java
index e834db4..6695b40 100644
--- a/testing/unittestsupport/applib/src/main/java/org/apache/isis/testing/unittestsupport/applib/core/soap/PublishedEndpoints.java
+++ b/testing/unittestsupport/applib/src/main/java/org/apache/isis/testing/unittestsupport/applib/core/soap/PublishedEndpoints.java
@@ -18,22 +18,115 @@
  */
 package org.apache.isis.testing.unittestsupport.applib.core.soap;
 
+import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
+import java.util.stream.Collectors;
 
 import org.apache.isis.commons.internal.base._Casts;
 import org.apache.isis.commons.internal.collections._Maps;
 
+import static org.apache.isis.commons.internal.base._NullSafe.stream;
+
+import lombok.val;
+
 /**
  * Collection of SOAP endpoints that have been published; will automatically assign a unique address to any
  * that have not been {@link SoapEndpoint}s whose {@link SoapEndpointSpec} does not specify an {@link SoapEndpointSpec#getEndpointAddress() address}.
+ *
+ * <p>
+ *     Intended to be used as a singleton, reused across multiple tests.  If necessary the singleton can be
+ *     {@link #dispose() dispose}d of.
+ * </p>
  */
-class PublishedEndpoints {
+public class PublishedEndpoints {
+
+    /**
+     * For any endpoints where the address is not specified, ports are assigned starting from this.
+     */
+    public static final int INITIAL_PORT_DEFAULT = 54345;
+
+    /**
+     * Lazily instantiates the singleton, using the {@link #INITIAL_PORT_DEFAULT default initial port}.
+     */
+    public static PublishedEndpoints instance() {
+        if(instance == null) {
+            return new PublishedEndpoints();
+        }
+        return instance;
+    }
+
+    /**
+     * Lazily instantiates the singleton, on specified port.
+     *
+     * <p>
+     *     If called again with a different port, then will {@link #dispose() discard} the singleton and start over.
+     * </p>
+     */
+    public static PublishedEndpoints instance(final int initialPort) {
+        if (instance != null) {
+            if (instance.port != initialPort) {
+                dispose();
+            }
+        }
+        if (instance == null) {
+            instance = new PublishedEndpoints(initialPort);
+        }
+        return instance;
+    }
+
+    /**
+     * Dispose of the singleton.
+     */
+    public static void dispose() {
+        instance = null;
+    }
 
-    private int port = SoapEndpointPublishingRule.INITIAL_PORT;
-    private Map<Class<?>, SoapEndpoint> soapEndpointByType = _Maps.newLinkedHashMap();
+    private static PublishedEndpoints instance;
+
+    PublishedEndpoints(){
+        this(INITIAL_PORT_DEFAULT);
+    }
+    PublishedEndpoints(int initialPort){
+        this.initialPort = initialPort;
+        this.port = this.initialPort;
+    }
+    private final int initialPort;
+    private int port;
+    private final Map<Class<?>, SoapEndpoint> soapEndpointByType = _Maps.newLinkedHashMap();
+
+
+    public PublishedEndpoints publishIfRequired(final Class<?> endpointClass, final String endpointAddress) {
+        return publishIfRequired(new SoapEndpointSpec(endpointClass, endpointAddress));
+    }
+
+    public PublishedEndpoints publishIfRequired(Class<?>... endpointClasses) {
+        val soapEndpointSpecs = stream(endpointClasses)
+                .map(SoapEndpointSpec::asSoapEndpointSpec)
+                .collect(Collectors.toCollection(ArrayList::new));
+        return publishIfRequired(soapEndpointSpecs);
+    }
+
+    public PublishedEndpoints publishIfRequired(final List<Class<?>> endpointClasses) {
+        val soapEndpointSpecs = stream(endpointClasses)
+                .map(SoapEndpointSpec::asSoapEndpointSpec)
+                .collect(Collectors.toCollection(ArrayList::new));
+        return publishIfRequired(soapEndpointSpecs);
+    }
+
+    public PublishedEndpoints publishIfRequired(SoapEndpointSpec... soapEndpointSpecs) {
+        val soapEndpointSpecs2 = stream(soapEndpointSpecs)
+                .collect(Collectors.toCollection(ArrayList::new));
+        return instance.publishIfRequired(soapEndpointSpecs2);
+    }
+
+    public PublishedEndpoints publishIfRequired(final Iterable<SoapEndpointSpec> soapEndpointSpecs) {
+        val soapEndpointSpecs2 = stream(soapEndpointSpecs)
+                .collect(Collectors.toCollection(ArrayList::new));
+        return instance.publishIfRequired(soapEndpointSpecs2);
+    }
 
-    void publishEndpointIfRequired(final List<SoapEndpointSpec> soapEndpointSpecs) {
+    public PublishedEndpoints publishEndpointIfRequired(final List<SoapEndpointSpec> soapEndpointSpecs) {
         // merge in any new endpoints to static cache
         for (SoapEndpointSpec soapEndpointSpec : soapEndpointSpecs) {
             final Class<?> endpointClass = soapEndpointSpec.getEndpointClass();
@@ -45,13 +138,14 @@ class PublishedEndpoints {
                 port = soapEndpoint.publish(port) + 1;
             }
         }
+        return this;
     }
 
-    String getEndpointAddress(Class<?> endpointClass) {
+    public String getEndpointAddress(Class<?> endpointClass) {
         return soapEndpointByType.get(endpointClass).getSpec().getEndpointAddress();
     }
 
-    <T> T getEndpointImplementor(Class<T> endpointClass) {
+    public <T> T getEndpointImplementor(Class<T> endpointClass) {
         return _Casts.uncheckedCast( soapEndpointByType.get(endpointClass).getImplementor() );
     }
 }
diff --git a/testing/unittestsupport/applib/src/main/java/org/apache/isis/testing/unittestsupport/applib/core/soap/SoapEndpointPublishingRule.java b/testing/unittestsupport/applib/src/main/java/org/apache/isis/testing/unittestsupport/applib/core/soap/SoapEndpointPublishingRule.java
index 9146b62..aa9d521 100644
--- a/testing/unittestsupport/applib/src/main/java/org/apache/isis/testing/unittestsupport/applib/core/soap/SoapEndpointPublishingRule.java
+++ b/testing/unittestsupport/applib/src/main/java/org/apache/isis/testing/unittestsupport/applib/core/soap/SoapEndpointPublishingRule.java
@@ -34,54 +34,49 @@ import static org.apache.isis.commons.internal.base._NullSafe.stream;
  */
 public class SoapEndpointPublishingRule implements TestRule {
 
-    /**
-     * For any endpoints where the address is not specified, ports are assigned starting from this.
-     */
-    static final int INITIAL_PORT = 54345;
-
-    private static PublishedEndpoints publishedEndpoints = new PublishedEndpoints();
-
-    private final List<SoapEndpointSpec> soapEndpointSpecs;
-
     public SoapEndpointPublishingRule(final Class<?> endpointClass, final String endpointAddress) {
-        this(new SoapEndpointSpec(endpointClass, endpointAddress));
+        PublishedEndpoints.instance().publishIfRequired(
+                new SoapEndpointSpec(endpointClass, endpointAddress));
     }
 
     public SoapEndpointPublishingRule(Class<?>... endpointClasses) {
-        this.soapEndpointSpecs = stream(endpointClasses)
-                .map(SoapEndpointSpec::asSoapEndpointSpec)
-                .collect(Collectors.toCollection(ArrayList::new));
+        PublishedEndpoints.instance().publishIfRequired(
+                stream(endpointClasses)
+                    .map(SoapEndpointSpec::asSoapEndpointSpec)
+                    .collect(Collectors.toCollection(ArrayList::new)));
     }
 
     public SoapEndpointPublishingRule(final List<Class<?>> endpointClasses) {
-        this.soapEndpointSpecs = stream(endpointClasses)
+        PublishedEndpoints.instance().publishIfRequired(
+                stream(endpointClasses)
                 .map(SoapEndpointSpec::asSoapEndpointSpec)
-                .collect(Collectors.toCollection(ArrayList::new));
+                .collect(Collectors.toCollection(ArrayList::new)));
     }
 
     public SoapEndpointPublishingRule(SoapEndpointSpec... soapEndpointSpecs) {
-        this.soapEndpointSpecs = stream(soapEndpointSpecs)
-                .collect(Collectors.toCollection(ArrayList::new));
+        PublishedEndpoints.instance().publishIfRequired(
+                stream(soapEndpointSpecs)
+                    .collect(Collectors.toCollection(ArrayList::new)));
     }
 
     public SoapEndpointPublishingRule(final Iterable<SoapEndpointSpec> soapEndpointSpecs) {
-        this.soapEndpointSpecs = stream(soapEndpointSpecs)
-                .collect(Collectors.toCollection(ArrayList::new));
+        PublishedEndpoints.instance().publishIfRequired(
+                stream(soapEndpointSpecs)
+                .collect(Collectors.toCollection(ArrayList::new)));
     }
 
     @Override
     public Statement apply(final Statement base, final Description description) {
-        publishedEndpoints.publishEndpointIfRequired(soapEndpointSpecs);
+        // now a no-op
         return base;
     }
 
     public String getEndpointAddress(Class<?> endpointClass) {
-        return publishedEndpoints.getEndpointAddress(endpointClass);
+        return PublishedEndpoints.instance().getEndpointAddress(endpointClass);
     }
 
     public <T> T getEndpointImplementor(Class<T> endpointClass) {
-        return publishedEndpoints.getEndpointImplementor(endpointClass);
+        return PublishedEndpoints.instance().getEndpointImplementor(endpointClass);
     }
 
-
 }