You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@isis.apache.org by ah...@apache.org on 2021/11/19 09:21:22 UTC

[isis] branch master updated: ISIS-2882: encapsulate heavy lifting for converter testing within ConverterTester

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

ahuber pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/isis.git


The following commit(s) were added to refs/heads/master by this push:
     new dcb9e58  ISIS-2882: encapsulate heavy lifting for converter testing within ConverterTester
dcb9e58 is described below

commit dcb9e58ba374ac81a0dfa228d975836ac68d7125
Author: andi-huber <ah...@apache.org>
AuthorDate: Fri Nov 19 10:21:12 2021 +0100

    ISIS-2882: encapsulate heavy lifting for converter testing within
    ConverterTester
---
 .../_testing/MetaModelContext_forTesting.java      |   4 +
 .../ui/components/scalars/ConverterTester.java     | 165 ++++++++++++++++++
 .../scalars/jdkmath/BigDecimalConverter.java       | 141 ++++++++++++++++
 .../jdkmath/BigDecimalConverter_roundtrip.java     | 186 ---------------------
 .../components/scalars/uuid/UuidConverterTest.java |  73 ++++++++
 .../scalars/uuid/UuidConverterTest_roundtrip.java  | 121 --------------
 6 files changed, 383 insertions(+), 307 deletions(-)

diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/_testing/MetaModelContext_forTesting.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/_testing/MetaModelContext_forTesting.java
index e0d8563..6e71b75 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/_testing/MetaModelContext_forTesting.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/_testing/MetaModelContext_forTesting.java
@@ -274,6 +274,10 @@ implements MetaModelContext {
     public ServiceInjector getServiceInjector() {
         if(serviceInjector==null) {
             serviceInjector = new ServiceInjector_forTesting(this);
+            // hotfix
+            if(specificationLoader==null) {
+                getSpecificationLoader();
+            }
         }
         return serviceInjector;
     }
diff --git a/viewers/wicket/ui/src/test/java/org/apache/isis/viewer/wicket/ui/components/scalars/ConverterTester.java b/viewers/wicket/ui/src/test/java/org/apache/isis/viewer/wicket/ui/components/scalars/ConverterTester.java
new file mode 100644
index 0000000..0b78d5b
--- /dev/null
+++ b/viewers/wicket/ui/src/test/java/org/apache/isis/viewer/wicket/ui/components/scalars/ConverterTester.java
@@ -0,0 +1,165 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.isis.viewer.wicket.ui.components.scalars;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.util.Locale;
+import java.util.Objects;
+
+import org.apache.wicket.util.convert.ConversionException;
+
+import org.apache.isis.applib.adapters.ValueSemanticsAbstract;
+import org.apache.isis.applib.services.iactnlayer.InteractionContext;
+import org.apache.isis.applib.services.iactnlayer.InteractionService;
+import org.apache.isis.commons.functional.ThrowingRunnable;
+import org.apache.isis.core.config.valuetypes.ValueSemanticsRegistry;
+import org.apache.isis.core.metamodel._testing.MetaModelContext_forTesting;
+import org.apache.isis.core.metamodel.commons.ScalarRepresentation;
+import org.apache.isis.core.metamodel.context.MetaModelContext;
+import org.apache.isis.core.security._testing.InteractionService_forTesting;
+import org.apache.isis.viewer.wicket.model.converter.ConverterBasedOnValueSemantics;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import lombok.AllArgsConstructor;
+import lombok.NonNull;
+import lombok.val;
+
+public class ConverterTester<T extends Serializable> {
+
+    private final Locale LOCALE_NOT_USED = null;
+
+    private final InteractionService interactionService;
+    private final MetaModelContext mmc;
+    private Scenario scenario;
+
+    @AllArgsConstructor
+    private class Scenario {
+        Locale locale;
+        ConverterBasedOnValueSemantics<T> converter;
+    }
+
+    public ConverterTester(final Class<T> valueType, final ValueSemanticsAbstract<T> valueSemantics) {
+
+        mmc = MetaModelContext_forTesting.builder()
+                .valueSemantic(valueSemantics)
+                .interactionProvider(interactionService = new InteractionService_forTesting())
+                .build();
+
+        mmc.getServiceInjector().injectServicesInto(valueSemantics);
+
+        // pre-requisites for testing
+        val reg = mmc.getServiceRegistry().lookupServiceElseFail(ValueSemanticsRegistry.class);
+        assertNotNull(reg.selectValueSemantics(valueType));
+        assertTrue(reg.selectValueSemantics(valueType).isNotEmpty());
+        assertNotNull(mmc.getServiceRegistry().lookupServiceElseFail(InteractionService.class));
+        assertNotNull(mmc.getInteractionProvider());
+    }
+
+    public void setScenario(
+            final @NonNull Locale locale,
+            final @NonNull ConverterBasedOnValueSemantics<T> converter) {
+        this.scenario = new Scenario(locale, converter);
+    }
+
+    public ConverterBasedOnValueSemantics<T> converterForProperty(
+            final Class<?> type,
+            final String propertyId,
+            final ScalarRepresentation representation) {
+        val customerSpec = mmc.getSpecificationLoader().specForTypeElseFail(type);
+        val prop = customerSpec.getPropertyElseFail("value");
+        return new ConverterBasedOnValueSemantics<>(prop, representation);
+    }
+
+    public void runWithInteraction(final @NonNull ThrowingRunnable runnable) {
+
+        Objects.requireNonNull(scenario, "must select a scenario before using this method");
+
+        interactionService.run(
+                InteractionContext.builder().locale(scenario.locale).build(),
+                runnable);
+    }
+
+    public void assertRoundtrip(
+            final @NonNull T value,
+            final @NonNull String valueAsText) {
+        assertRoundtrip(value, valueAsText, valueAsText);
+    }
+
+    /**
+     * @param value - non-null
+     * @param valueAsText - parser input
+     * @param expectedText - reversed parser output
+     */
+    public void assertRoundtrip(
+            final @NonNull T value,
+            final @NonNull String valueAsText,
+            final @NonNull String expectedText) {
+        runWithInteraction(()->{
+
+            if(value instanceof BigDecimal) {
+
+                assertNumberEquals(
+                        (BigDecimal)value, (BigDecimal)scenario.converter.convertToObject(valueAsText, LOCALE_NOT_USED));
+
+            } else {
+                assertEquals(
+                        value, scenario.converter.convertToObject(valueAsText, LOCALE_NOT_USED));
+            }
+
+            assertEquals(
+                    expectedText, scenario.converter.convertToString(value, LOCALE_NOT_USED));
+        });
+    }
+
+    public void assertHandlesEmpty() {
+        runWithInteraction(()->{
+            assertNull(scenario.converter.convertToObject(null, LOCALE_NOT_USED));
+            assertNull(scenario.converter.convertToObject("", LOCALE_NOT_USED));
+            assertNull(scenario.converter.convertToString(null, LOCALE_NOT_USED));
+        });
+    }
+
+    public void assertConversionFailure(
+            final @NonNull String valueAsInvalidText,
+            final @NonNull String expectedMessage) {
+
+        runWithInteraction(()->{
+            val ex = assertThrows(ConversionException.class, //TODO find a more generic exception type
+                    ()->scenario.converter.convertToObject(valueAsInvalidText, LOCALE_NOT_USED));
+            assertEquals(expectedMessage, ex.getMessage());
+        });
+    }
+
+    // -- HELPER
+
+    private static void assertNumberEquals(final BigDecimal a, final BigDecimal b) {
+        val maxScale = Math.max(a.scale(), b.scale());
+        assertEquals(
+                a.setScale(maxScale),
+                b.setScale(maxScale));
+    }
+
+
+}
diff --git a/viewers/wicket/ui/src/test/java/org/apache/isis/viewer/wicket/ui/components/scalars/jdkmath/BigDecimalConverter.java b/viewers/wicket/ui/src/test/java/org/apache/isis/viewer/wicket/ui/components/scalars/jdkmath/BigDecimalConverter.java
new file mode 100644
index 0000000..0207660
--- /dev/null
+++ b/viewers/wicket/ui/src/test/java/org/apache/isis/viewer/wicket/ui/components/scalars/jdkmath/BigDecimalConverter.java
@@ -0,0 +1,141 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.isis.viewer.wicket.ui.components.scalars.jdkmath;
+
+import java.math.BigDecimal;
+import java.util.Locale;
+
+import javax.validation.constraints.Digits;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import org.apache.isis.applib.annotation.DomainObject;
+import org.apache.isis.applib.annotation.Property;
+import org.apache.isis.core.metamodel.commons.ScalarRepresentation;
+import org.apache.isis.core.metamodel.valuesemantics.BigDecimalValueSemantics;
+import org.apache.isis.viewer.wicket.model.converter.ConverterBasedOnValueSemantics;
+import org.apache.isis.viewer.wicket.ui.components.scalars.ConverterTester;
+
+import lombok.Getter;
+import lombok.Setter;
+
+class BigDecimalConverter {
+
+    final BigDecimal bd_123_45_scale2 = new BigDecimal("123.45").setScale(2);
+    final BigDecimal bd_123_4500_scale2 = new BigDecimal("123.4500").setScale(2);
+
+    final BigDecimal bd_789123_45_scale2 = new BigDecimal("789123.45").setScale(2);
+
+    final BigDecimal bd_123_45_scale4 = new BigDecimal("123.45").setScale(4);
+    final BigDecimal bd_123_4500_scale4 = new BigDecimal("123.4500").setScale(4);
+
+    private ConverterTester<BigDecimal> converterTester;
+
+    @BeforeEach
+    void setUp() throws Exception {
+        this.converterTester = new ConverterTester<>(BigDecimal.class, new BigDecimalValueSemantics());
+    }
+
+    @Test
+    void scale2_english() {
+        converterTester.setScenario(Locale.ENGLISH, newConverter(CustomerScale2.class));
+        converterTester.assertRoundtrip(bd_123_45_scale2, "123.45");
+    }
+
+    @Test
+    void scale4_english() {
+        converterTester.setScenario(Locale.ENGLISH, newConverter(CustomerScale4.class));
+        converterTester.assertRoundtrip(bd_123_45_scale4, "123.4500", "123.45");
+
+    }
+
+    @Test
+    void scaleNull_english() {
+        converterTester.setScenario(Locale.ENGLISH, newConverter(CustomerScaleNone.class));
+        converterTester.assertRoundtrip(bd_123_45_scale4, "123.45");
+
+        converterTester.setScenario(Locale.ENGLISH, newConverter(CustomerScaleNone.class));
+        converterTester.assertRoundtrip(bd_123_45_scale4, "123.4500", "123.45");
+    }
+
+    @Test
+    void scale2_italian() {
+        converterTester.setScenario(Locale.ITALIAN, newConverter(CustomerScaleNone.class));
+        converterTester.assertRoundtrip(bd_123_45_scale4, "123,45");
+    }
+
+    @Test
+    void scale2_english_withThousandSeparators() {
+        converterTester.setScenario(Locale.ENGLISH, newConverter(CustomerScale2.class));
+        converterTester.assertRoundtrip(bd_789123_45_scale2, "789,123.45");
+    }
+
+    @Test
+    void scale2_english_withoutThousandSeparators() {
+        converterTester.setScenario(Locale.ENGLISH, newConverter(CustomerScale2.class));
+        converterTester.assertRoundtrip(bd_789123_45_scale2, "789123.45", "789,123.45");
+    }
+
+    @Test
+    void scale2_english_tooLargeScale() {
+        converterTester.setScenario(Locale.ENGLISH, newConverter(CustomerScale2.class));
+        converterTester.assertConversionFailure("123.454",
+                "No more than 2 digits can be entered after the decimal separator, got 3 in '123.454'.");
+    }
+
+    @Test
+    void when_null() {
+        converterTester.setScenario(Locale.ENGLISH, newConverter(CustomerScale2.class));
+        converterTester.assertHandlesEmpty();
+    }
+
+    @Test
+    void invalid() {
+        converterTester.setScenario(Locale.ENGLISH, newConverter(CustomerScale2.class));
+        converterTester.assertConversionFailure("junk", "Not a decimal value 'junk': could not parse input='junk'");
+    }
+
+    // -- SCENARIOS
+
+    @DomainObject
+    static class CustomerScaleNone {
+        @Property @Getter @Setter
+        private BigDecimal value;
+    }
+
+    @DomainObject
+    static class CustomerScale2 {
+        @Property @Getter @Setter
+        @Digits(fraction = 2, integer = 20)
+        private BigDecimal value;
+    }
+
+    @DomainObject
+    static class CustomerScale4 {
+        @Property @Getter @Setter
+        @Digits(fraction = 4, integer = 20)
+        private BigDecimal value;
+    }
+
+    private ConverterBasedOnValueSemantics<BigDecimal> newConverter(final Class<?> type) {
+        return converterTester.converterForProperty(type, "value", ScalarRepresentation.EDITING);
+    }
+
+}
diff --git a/viewers/wicket/ui/src/test/java/org/apache/isis/viewer/wicket/ui/components/scalars/jdkmath/BigDecimalConverter_roundtrip.java b/viewers/wicket/ui/src/test/java/org/apache/isis/viewer/wicket/ui/components/scalars/jdkmath/BigDecimalConverter_roundtrip.java
deleted file mode 100644
index 74df160..0000000
--- a/viewers/wicket/ui/src/test/java/org/apache/isis/viewer/wicket/ui/components/scalars/jdkmath/BigDecimalConverter_roundtrip.java
+++ /dev/null
@@ -1,186 +0,0 @@
-/*
- *  Licensed to the Apache Software Foundation (ASF) under one
- *  or more contributor license agreements.  See the NOTICE file
- *  distributed with this work for additional information
- *  regarding copyright ownership.  The ASF licenses this file
- *  to you under the Apache License, Version 2.0 (the
- *  "License"); you may not use this file except in compliance
- *  with the License.  You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing,
- *  software distributed under the License is distributed on an
- *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- *  KIND, either express or implied.  See the License for the
- *  specific language governing permissions and limitations
- *  under the License.
- */
-package org.apache.isis.viewer.wicket.ui.components.scalars.jdkmath;
-
-import java.math.BigDecimal;
-import java.util.Locale;
-
-import javax.validation.constraints.Digits;
-
-import org.apache.wicket.util.convert.ConversionException;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-
-import org.apache.isis.applib.annotation.DomainObject;
-import org.apache.isis.applib.annotation.Property;
-import org.apache.isis.applib.services.iactnlayer.InteractionContext;
-import org.apache.isis.applib.services.iactnlayer.InteractionService;
-import org.apache.isis.core.config.valuetypes.ValueSemanticsRegistry;
-import org.apache.isis.core.metamodel._testing.MetaModelContext_forTesting;
-import org.apache.isis.core.metamodel.commons.ScalarRepresentation;
-import org.apache.isis.core.metamodel.context.MetaModelContext;
-import org.apache.isis.core.metamodel.valuesemantics.BigDecimalValueSemantics;
-import org.apache.isis.core.security._testing.InteractionService_forTesting;
-import org.apache.isis.viewer.wicket.model.converter.ConverterBasedOnValueSemantics;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertNotNull;
-import static org.junit.jupiter.api.Assertions.assertThrows;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-
-import lombok.Getter;
-import lombok.Setter;
-import lombok.val;
-
-class BigDecimalConverter_roundtrip {
-
-    final BigDecimal bd_123_45_scale2 = new BigDecimal("123.45").setScale(2);
-    final BigDecimal bd_123_4500_scale2 = new BigDecimal("123.4500").setScale(2);
-
-    final BigDecimal bd_789123_45_scale2 = new BigDecimal("789123.45").setScale(2);
-
-    final BigDecimal bd_123_45_scale4 = new BigDecimal("123.45").setScale(4);
-    final BigDecimal bd_123_4500_scale4 = new BigDecimal("123.4500").setScale(4);
-
-    private InteractionService interactionService;
-    private MetaModelContext mmc;
-
-    @BeforeEach
-    void setUp() throws Exception {
-
-        BigDecimalValueSemantics valueSemantics;
-        mmc = MetaModelContext_forTesting.builder()
-                .valueSemantic(valueSemantics = new BigDecimalValueSemantics())
-                .interactionProvider(interactionService = new InteractionService_forTesting())
-                .build();
-        valueSemantics.setSpecificationLoader(mmc.getSpecificationLoader());
-
-        // pre-requisites for testing
-        val reg = mmc.getServiceRegistry().lookupServiceElseFail(ValueSemanticsRegistry.class);
-        assertNotNull(reg.selectValueSemantics(BigDecimal.class));
-        assertTrue(reg.selectValueSemantics(BigDecimal.class).isNotEmpty());
-        assertNotNull(mmc.getServiceRegistry().lookupServiceElseFail(InteractionService.class));
-        assertNotNull(mmc.getInteractionProvider());
-    }
-
-    @Test
-    void scale2_english() {
-        assertRoundtrip(CustomerScale2.class, bd_123_45_scale2, "123.45", "123.45", Locale.ENGLISH);
-    }
-
-    @Test
-    void scale4_english() {
-        assertRoundtrip(CustomerScale4.class, bd_123_45_scale4, "123.4500", "123.45", Locale.ENGLISH);
-    }
-
-    @Test
-    void scaleNull_english() {
-        assertRoundtrip(CustomerScaleNone.class, bd_123_45_scale2, "123.45", "123.45", Locale.ENGLISH);
-        assertRoundtrip(CustomerScaleNone.class, bd_123_45_scale4, "123.4500", "123.45", Locale.ENGLISH);
-    }
-
-    @Test
-    void scale2_italian() {
-        assertRoundtrip(CustomerScaleNone.class, bd_123_45_scale2, "123,45", "123,45", Locale.ITALIAN);
-    }
-
-    @Test
-    void scale2_english_withThousandSeparators() {
-        assertRoundtrip(CustomerScale2.class, bd_789123_45_scale2, "789,123.45", "789,123.45", Locale.ENGLISH);
-    }
-
-    @Test
-    void scale2_english_withoutThousandSeparators() {
-        assertRoundtrip(CustomerScale2.class, bd_789123_45_scale2, "789123.45", "789,123.45", Locale.ENGLISH);
-    }
-
-    @Test
-    void scale2_english_tooLargeScale() {
-        assertParseError(CustomerScale2.class, "123.454", Locale.ENGLISH,
-                "No more than 2 digits can be entered after the decimal separator, got 3 in '123.454'.");
-    }
-
-    // -- HELPER
-
-    private void assertRoundtrip(final Class<?> type, final BigDecimal expected, final String valueAsText, final String expectedText, final Locale locale) {
-
-        val converter = newConverter(type);
-
-        interactionService.run(InteractionContext.builder()
-                .locale(locale)
-                .build(), ()->{
-
-                    // when
-                    final BigDecimal actual = converter.convertToObject(valueAsText, null);
-                    assertNumberEquals(expected, actual);
-
-                    // when
-                    final String actualStr = converter.convertToString(actual, null);
-                    assertEquals(expectedText, actualStr);
-
-                });
-    }
-
-    private void assertParseError(final Class<?> type, final String valueAsText, final Locale locale, final String expectedMessage) {
-
-        val converter = newConverter(type);
-
-        interactionService.run(InteractionContext.builder()
-                .locale(locale)
-                .build(), ()->{
-                    // when
-                    val ex = assertThrows(ConversionException.class, ()->
-                        converter.convertToObject(valueAsText, null));
-                    assertEquals(expectedMessage, ex.getMessage());
-                });
-    }
-
-    private static void assertNumberEquals(final BigDecimal a, final BigDecimal b) {
-        assertEquals(a, b);
-    }
-
-    // -- SCENARIOS
-
-    @DomainObject
-    static class CustomerScaleNone {
-        @Property @Getter @Setter
-        private BigDecimal value;
-    }
-
-    @DomainObject
-    static class CustomerScale2 {
-        @Property @Getter @Setter
-        @Digits(fraction = 2, integer = 20)
-        private BigDecimal value;
-    }
-
-    @DomainObject
-    static class CustomerScale4 {
-        @Property @Getter @Setter
-        @Digits(fraction = 4, integer = 20)
-        private BigDecimal value;
-    }
-
-    private ConverterBasedOnValueSemantics<BigDecimal> newConverter(final Class<?> type) {
-        val customerSpec = mmc.getSpecificationLoader().specForTypeElseFail(type);
-        val prop = customerSpec.getPropertyElseFail("value");
-        return new ConverterBasedOnValueSemantics<>(prop, ScalarRepresentation.EDITING);
-    }
-
-}
diff --git a/viewers/wicket/ui/src/test/java/org/apache/isis/viewer/wicket/ui/components/scalars/uuid/UuidConverterTest.java b/viewers/wicket/ui/src/test/java/org/apache/isis/viewer/wicket/ui/components/scalars/uuid/UuidConverterTest.java
new file mode 100644
index 0000000..82db96a
--- /dev/null
+++ b/viewers/wicket/ui/src/test/java/org/apache/isis/viewer/wicket/ui/components/scalars/uuid/UuidConverterTest.java
@@ -0,0 +1,73 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.isis.viewer.wicket.ui.components.scalars.uuid;
+
+import java.util.Locale;
+import java.util.UUID;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import org.apache.isis.applib.annotation.DomainObject;
+import org.apache.isis.applib.annotation.Property;
+import org.apache.isis.core.metamodel.commons.ScalarRepresentation;
+import org.apache.isis.core.metamodel.valuesemantics.UUIDValueSemantics;
+import org.apache.isis.viewer.wicket.ui.components.scalars.ConverterTester;
+
+import lombok.Getter;
+import lombok.Setter;
+
+class UuidConverterTest {
+
+    final UUID valid = UUID.randomUUID();
+    ConverterTester<UUID> converterTester;
+
+    @BeforeEach
+    void setUp() throws Exception {
+        converterTester = new ConverterTester<>(UUID.class, new UUIDValueSemantics());
+        converterTester.setScenario(
+                Locale.ENGLISH,
+                converterTester.converterForProperty(
+                        CustomerWithUuid.class, "value", ScalarRepresentation.EDITING));
+    }
+
+    @Test
+    void happy_case() {
+        converterTester.assertRoundtrip(valid, valid.toString());
+    }
+
+    @Test
+    void when_null() {
+        converterTester.assertHandlesEmpty();
+    }
+
+    @Test
+    void invalid() {
+        converterTester.assertConversionFailure("junk", "Failed to convert 'junk' to a UUID");
+    }
+
+    // -- SCENARIOS
+
+    @DomainObject
+    static class CustomerWithUuid {
+        @Property @Getter @Setter
+        private UUID value;
+    }
+
+}
diff --git a/viewers/wicket/ui/src/test/java/org/apache/isis/viewer/wicket/ui/components/scalars/uuid/UuidConverterTest_roundtrip.java b/viewers/wicket/ui/src/test/java/org/apache/isis/viewer/wicket/ui/components/scalars/uuid/UuidConverterTest_roundtrip.java
deleted file mode 100644
index 1049e3c..0000000
--- a/viewers/wicket/ui/src/test/java/org/apache/isis/viewer/wicket/ui/components/scalars/uuid/UuidConverterTest_roundtrip.java
+++ /dev/null
@@ -1,121 +0,0 @@
-/*
- *  Licensed to the Apache Software Foundation (ASF) under one
- *  or more contributor license agreements.  See the NOTICE file
- *  distributed with this work for additional information
- *  regarding copyright ownership.  The ASF licenses this file
- *  to you under the Apache License, Version 2.0 (the
- *  "License"); you may not use this file except in compliance
- *  with the License.  You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing,
- *  software distributed under the License is distributed on an
- *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- *  KIND, either express or implied.  See the License for the
- *  specific language governing permissions and limitations
- *  under the License.
- */
-package org.apache.isis.viewer.wicket.ui.components.scalars.uuid;
-
-import java.util.Locale;
-import java.util.UUID;
-
-import org.apache.wicket.util.convert.ConversionException;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-
-import org.apache.isis.applib.annotation.DomainObject;
-import org.apache.isis.applib.annotation.Property;
-import org.apache.isis.applib.services.iactnlayer.InteractionService;
-import org.apache.isis.core.config.valuetypes.ValueSemanticsRegistry;
-import org.apache.isis.core.metamodel._testing.MetaModelContext_forTesting;
-import org.apache.isis.core.metamodel.commons.ScalarRepresentation;
-import org.apache.isis.core.metamodel.context.MetaModelContext;
-import org.apache.isis.core.metamodel.valuesemantics.UUIDValueSemantics;
-import org.apache.isis.core.security._testing.InteractionService_forTesting;
-import org.apache.isis.viewer.wicket.model.converter.ConverterBasedOnValueSemantics;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertNotNull;
-import static org.junit.jupiter.api.Assertions.assertNull;
-import static org.junit.jupiter.api.Assertions.assertThrows;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-
-import lombok.Getter;
-import lombok.Setter;
-import lombok.val;
-
-// see also BigDecimalConverter_roundtrip, for more rigorous testing
-@SuppressWarnings("unused")
-class UuidConverterTest_roundtrip {
-
-    final UUID valid = UUID.randomUUID();
-
-    private ConverterBasedOnValueSemantics<UUID> converter;
-
-    private InteractionService interactionService;
-    private MetaModelContext mmc;
-
-    @BeforeEach
-    void setUp() throws Exception {
-
-        UUIDValueSemantics valueSemantics;
-        mmc = MetaModelContext_forTesting.builder()
-                .valueSemantic(valueSemantics = new UUIDValueSemantics())
-                .interactionProvider(interactionService = new InteractionService_forTesting())
-                .build();
-        //valueSemantics.setSpecificationLoader(mmc.getSpecificationLoader());
-
-        // pre-requisites for testing
-        val reg = mmc.getServiceRegistry().lookupServiceElseFail(ValueSemanticsRegistry.class);
-        assertNotNull(reg.selectValueSemantics(UUID.class));
-        assertTrue(reg.selectValueSemantics(UUID.class).isNotEmpty());
-        assertNotNull(mmc.getServiceRegistry().lookupServiceElseFail(InteractionService.class));
-        assertNotNull(mmc.getInteractionProvider());
-
-
-        converter = newConverter(CustomerWithUuid.class);
-    }
-
-    @Test
-    void happy_case() {
-
-        assertEquals(
-                valid, converter.convertToObject(valid.toString(), Locale.ENGLISH));
-        assertEquals(
-                valid.toString(), converter.convertToString(valid, Locale.ENGLISH));
-    }
-
-    @Test
-    void when_null() {
-        assertNull(converter.convertToObject(null, Locale.ENGLISH));
-        assertNull(converter.convertToObject("", Locale.ENGLISH));
-        assertNull(converter.convertToString(null, Locale.ENGLISH));
-    }
-
-    @Test
-    void invalid() {
-        assertThrows(ConversionException.class,
-                ()->converter.convertToObject("junk", Locale.ENGLISH),
-                "Failed to convert 'junk' to a UUID");
-    }
-
-    // -- HELPER
-
-    private ConverterBasedOnValueSemantics<UUID> newConverter(final Class<?> type) {
-        val customerSpec = mmc.getSpecificationLoader().specForTypeElseFail(type);
-        val prop = customerSpec.getPropertyElseFail("value");
-        return new ConverterBasedOnValueSemantics<>(prop, ScalarRepresentation.EDITING);
-    }
-
-    // -- SCENARIOS
-
-    @DomainObject
-    static class CustomerWithUuid {
-        @Property @Getter @Setter
-        private UUID value;
-    }
-
-
-}