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

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


The following commit(s) were added to refs/heads/master by this push:
     new a1c408d5a Test modernization
a1c408d5a is described below

commit a1c408d5a55f81fb2449327f9a9ab4cab27fd019
Author: James Bognar <[email protected]>
AuthorDate: Thu Sep 11 12:20:50 2025 -0400

    Test modernization
---
 .../src/test/java/org/apache/juneau/TestUtils.java |  10 +-
 .../java/org/apache/juneau/cp/BeanStore_Test.java  |   8 +-
 .../juneau/dto/swagger/ResponseInfo_Test.java      |   2 +-
 .../java/org/apache/juneau/junit/Assertions2.java  |  65 +--
 .../apache/juneau/junit/BasicBeanConverter.java    | 165 ++----
 .../juneau/junit/BasicBeanConverter_Test.java      |  16 +-
 .../java/org/apache/juneau/junit/Listifiers.java   | 346 +++++++++++
 .../org/apache/juneau/junit/Listifiers_Test.java   | 411 +++++++++++++
 .../apache/juneau/junit/PropertyExtractors.java    |   1 +
 .../juneau/junit/PropertyExtractors_Test.java      |  12 +-
 .../java/org/apache/juneau/junit/Stringifiers.java | 650 +++++++++++++++++++++
 .../org/apache/juneau/junit/Stringifiers_Test.java | 529 +++++++++++++++++
 .../java/org/apache/juneau/junit/Swappers.java     | 213 +++++++
 .../org/apache/juneau/junit/Swappers_Test.java     | 227 +++++++
 .../test/java/org/apache/juneau/junit/Utils.java   |  20 +-
 .../java/org/apache/juneau/junit/Utils_Test.java   |  70 ++-
 16 files changed, 2537 insertions(+), 208 deletions(-)

diff --git a/juneau-utest/src/test/java/org/apache/juneau/TestUtils.java 
b/juneau-utest/src/test/java/org/apache/juneau/TestUtils.java
index 7344e6e1a..cf97e1d95 100644
--- a/juneau-utest/src/test/java/org/apache/juneau/TestUtils.java
+++ b/juneau-utest/src/test/java/org/apache/juneau/TestUtils.java
@@ -316,10 +316,6 @@ public class TestUtils extends Utils2 {
                Assertions2.assertBean(actual, fields, expected);
        }
 
-       private static List<NestedTokenizer.Token> tokenize(String fields) {
-               return NestedTokenizer.tokenize(fields);
-       }
-
        /**
         * Asserts that multiple beans in a collection have the expected 
property values.
         *
@@ -633,10 +629,10 @@ public class TestUtils extends Utils2 {
        }
 
        /**
-        * Asserts value when stringified matches the specified pattern.
+        * Asserts value when stringified matches the specified glob-style 
pattern.
         */
-       public static void assertMatches(String pattern, Object value) {
-               Assertions2.assertMatches(pattern, value);
+       public static void assertMatchesGlob(String pattern, Object value) {
+               Assertions2.assertMatchesGlob(pattern, value);
        }
 
        /**
diff --git 
a/juneau-utest/src/test/java/org/apache/juneau/cp/BeanStore_Test.java 
b/juneau-utest/src/test/java/org/apache/juneau/cp/BeanStore_Test.java
index 0973d7097..7373aa4a4 100644
--- a/juneau-utest/src/test/java/org/apache/juneau/cp/BeanStore_Test.java
+++ b/juneau-utest/src/test/java/org/apache/juneau/cp/BeanStore_Test.java
@@ -156,10 +156,10 @@ class BeanStore_Test extends SimpleTestBase {
                        assertList(b.stream(A2.class).map(BeanStoreEntry::get), 
a2a);
                }
 
-               
assertMatches("{*,entries:[{type:'A1',bean:'"+identity(a1b)+"'},{type:'A1',bean:'"+identity(a1a)+"'}]}",
 b1p);
-               
assertMatches("{*,entries:[{type:'A2',bean:'"+identity(a2a)+"'}],parent:{*,entries:[{type:'A1',bean:'"+identity(a1b)+"'},{type:'A1',bean:'"+identity(a1a)+"'}]}}",
 b1c);
-               
assertMatches("{*,entries:[{type:'A1',bean:'"+identity(a1b)+"'},{type:'A1',bean:'"+identity(a1a)+"'}],threadSafe:true}",
 b2p);
-               
assertMatches("{*,entries:[{type:'A2',bean:'"+identity(a2a)+"'}],parent:{*,entries:[{type:'A1',bean:'"+identity(a1b)+"'},{type:'A1',bean:'"+identity(a1a)+"'}],threadSafe:true},threadSafe:true}",
 b2c);
+               
assertMatchesGlob("{*,entries:[{type:'A1',bean:'"+identity(a1b)+"'},{type:'A1',bean:'"+identity(a1a)+"'}]}",
 b1p);
+               
assertMatchesGlob("{*,entries:[{type:'A2',bean:'"+identity(a2a)+"'}],parent:{*,entries:[{type:'A1',bean:'"+identity(a1b)+"'},{type:'A1',bean:'"+identity(a1a)+"'}]}}",
 b1c);
+               
assertMatchesGlob("{*,entries:[{type:'A1',bean:'"+identity(a1b)+"'},{type:'A1',bean:'"+identity(a1a)+"'}],threadSafe:true}",
 b2p);
+               
assertMatchesGlob("{*,entries:[{type:'A2',bean:'"+identity(a2a)+"'}],parent:{*,entries:[{type:'A1',bean:'"+identity(a1b)+"'},{type:'A1',bean:'"+identity(a1a)+"'}],threadSafe:true},threadSafe:true}",
 b2c);
 
                b1p.removeBean(A1.class);
                b1c.clear().addBean(A1.class, a1a);
diff --git 
a/juneau-utest/src/test/java/org/apache/juneau/dto/swagger/ResponseInfo_Test.java
 
b/juneau-utest/src/test/java/org/apache/juneau/dto/swagger/ResponseInfo_Test.java
index 9ca403e86..386ee618c 100644
--- 
a/juneau-utest/src/test/java/org/apache/juneau/dto/swagger/ResponseInfo_Test.java
+++ 
b/juneau-utest/src/test/java/org/apache/juneau/dto/swagger/ResponseInfo_Test.java
@@ -60,7 +60,7 @@ class ResponseInfo_Test extends SimpleTestBase {
                );
                assertMap(
                        t.setExamples(map()).addExample("text/a", 
"a").addExample("text/b", null).addExample(null, "c").getExamples(),
-                       "text/a,text/b,<NULL>",
+                       "text/a,text/b,<null>",
                        "a,<null>,c"
                );
        }
diff --git 
a/juneau-utest/src/test/java/org/apache/juneau/junit/Assertions2.java 
b/juneau-utest/src/test/java/org/apache/juneau/junit/Assertions2.java
index 72e8ea97f..f8c073857 100644
--- a/juneau-utest/src/test/java/org/apache/juneau/junit/Assertions2.java
+++ b/juneau-utest/src/test/java/org/apache/juneau/junit/Assertions2.java
@@ -870,13 +870,13 @@ public class Assertions2 {
         * <h5 class='section'>Usage Examples:</h5>
         * <p class='bjava'>
         *      <jc>// Test string length</jc>
-        *      assertSize(5, <js>"hello"</js>);
+        *      <jsm>assertSize</jsm>(5, <js>"hello"</js>);
         *
         *      <jc>// Test collection size</jc>
-        *      assertSize(3, List.<jsm>of</jsm>(<js>"a"</js>, <js>"b"</js>, 
<js>"c"</js>));
+        *      <jsm>assertSize</jsm>(3, List.<jsm>of</jsm>(<js>"a"</js>, 
<js>"b"</js>, <js>"c"</js>));
         *
         *      <jc>// Test array size</jc>
-        *      assertSize(2, <jk>new</jk> String[]{<js>"x"</js>, 
<js>"y"</js>});
+        *      <jsm>assertSize</jsm>(2, <jk>new</jk> String[]{<js>"x"</js>, 
<js>"y"</js>});
         * </p>
         *
         * @param expected The expected size/length.
@@ -919,23 +919,23 @@ public class Assertions2 {
         * <h5 class='section'>Usage Examples:</h5>
         * <p class='bjava'>
         *      <jc>// Test exact string conversion</jc>
-        *      <jsm>assertString</jsm>(<js>"John,30,true"</js>, user); <jc>// 
Assuming user converts to this format</jc>
+        *      <jsm>assertString</jsm>(<js>"John,30,true"</js>, 
<jv>user</jv>); <jc>// Assuming user converts to this format</jc>
         *
         *      <jc>// Test formatted dates or numbers</jc>
-        *      <jsm>assertString</jsm>(<js>"2023-12-01"</js>, localDate);
+        *      <jsm>assertString</jsm>(<js>"2023-12-01"</js>, 
<jv>localDate</jv>);
         *
         *      <jc>// Test complex object serialization</jc>
-        *      <jsm>assertString</jsm>(<js>"{name=John,age=30}"</js>, userMap);
+        *      <jsm>assertString</jsm>(<js>"{name=John,age=30}"</js>, 
<jv>userMap</jv>);
         *
         *      <jc>// Test array/collection formatting</jc>
-        *      <jsm>assertString</jsm>(<js>"[red,green,blue]"</js>, colors);
+        *      <jsm>assertString</jsm>(<js>"[red,green,blue]"</js>, 
<jv>colors</jv>);
         * </p>
         *
         * @param expected The exact string that the actual object should 
convert to
         * @param actual The object to test. Must not be null.
         * @throws AssertionError if the actual object is null or its string 
representation doesn't exactly match expected
         * @see #assertContains(String, Object) for partial string matching
-        * @see #assertMatches(String, Object) for pattern-based matching
+        * @see #assertMatchesGlob(String, Object) for pattern-based matching
         */
        public static void assertString(String expected, Object actual) {
                assertString(args(), expected, actual);
@@ -958,62 +958,59 @@ public class Assertions2 {
        }
 
        /**
-        * Asserts that an object's string representation matches the specified 
regular expression pattern.
+        * Asserts that an object's string representation matches the specified 
glob-style pattern.
         *
         * <p>This method converts the actual object to its string 
representation using the current
-        * {@link BeanConverter} and then tests it against the provided regular 
expression pattern.
-        * This is useful for testing string formats, validating patterns, or 
checking flexible formats.</p>
+        * {@link BeanConverter} and then tests it against the provided 
glob-style pattern.
+        * This is useful for testing string formats with simple wildcard 
patterns.</p>
         *
         * <h5 class='section'>Pattern Syntax:</h5>
-        * <p>The pattern parameter supports:</p>
+        * <p>The pattern uses glob-style wildcards:</p>
         * <ul>
-        *      <li><b>Standard regex:</b> Full Java regular expression 
syntax</li>
-        *      <li><b>Case-insensitive:</b> Patterns ending with "/i" are 
automatically made case-insensitive</li>
-        *      <li><b>Dotall mode:</b> Patterns ending with "/s" enable dotall 
mode (. matches newlines)</li>
-        *      <li><b>Combined flags:</b> Use "/is" for both case-insensitive 
and dotall modes</li>
+        *      <li><b>{@code *}</b> matches any sequence of characters 
(including none)</li>
+        *      <li><b>{@code ?}</b> matches exactly one character</li>
+        *      <li><b>All other characters</b> are treated literally</li>
         * </ul>
         *
         * <h5 class='section'>Usage Examples:</h5>
         * <p class='bjava'>
-        *      <jc>// Test email format</jc>
-        *      <jsm>assertMatches</jsm>(<js>"\\w+@\\w+\\.\\w+"</js>, 
user.getEmail());
+        *      <jc>// Test filename patterns</jc>
+        *      <jsm>assertMatchesGlob</jsm>(<js>"user_*_temp"</js>, 
<jv>filename</jv>);
         *
-        *      <jc>// Test case-insensitive pattern</jc>
-        *      <jsm>assertMatches</jsm>(<js>"error.*timeout/i"</js>, 
logMessage);
+        *      <jc>// Test single character wildcards</jc>
+        *      <jsm>assertMatchesGlob</jsm>(<js>"file?.txt"</js>, 
<jv>fileName</jv>);
         *
-        *      <jc>// Test flexible number format</jc>
-        *      
<jsm>assertMatches</jsm>(<js>"\\d{1,3}(,\\d{3})*(\\.\\d{2})?"</js>, 
formattedPrice);
-        *
-        *      <jc>// Test multiline content with dotall mode</jc>
-        *      <jsm>assertMatches</jsm>(<js>".*error.*exception.*"</js>, 
<jv>multiLineLog</jv>);
+        *      <jc>// Test combined patterns</jc>
+        *      <jsm>assertMatchesGlob</jsm>(<js>"log_*_?.txt"</js>, 
<jv>logFile</jv>);
         * </p>
         *
-        * @param pattern The regular expression pattern to match against. 
Supports "/i" and "/s" flags.
+        * @param pattern The glob-style pattern to match against.
         * @param value The object to test. Must not be null.
         * @throws AssertionError if the value is null or its string 
representation doesn't match the pattern
         * @see #assertString(String, Object) for exact string matching
         * @see #assertContains(String, Object) for substring matching
+        * @see Utils#getGlobMatchPattern(String) for pattern compilation 
details
         */
-       public static void assertMatches(String pattern, Object value) {
-               assertMatches(args(), pattern, value);
+       public static void assertMatchesGlob(String pattern, Object value) {
+               assertMatchesGlob(args(), pattern, value);
        }
 
        /**
-        * Same as {@link #assertMatches(String, Object)} but with configurable 
assertion behavior.
+        * Same as {@link #assertMatchesGlob(String, Object)} but with 
configurable assertion behavior.
         *
         * @param args Assertion configuration. See {@link #args()} for usage 
examples.
-        * @param pattern The regular expression pattern to match against.
+        * @param pattern The glob-style pattern to match against.
         * @param value The object to test. Must not be null.
-        * @see #assertMatches(String, Object)
+        * @see #assertMatchesGlob(String, Object)
         * @see #args()
         */
-       public static void assertMatches(AssertionArgs args, String pattern, 
Object value) {
+       public static void assertMatchesGlob(AssertionArgs args, String 
pattern, Object value) {
                assertArgNotNull("args", args);
                assertArgNotNull("pattern", pattern);
                assertNotNull(value, "Value was null.");
 
                var v = 
args.getBeanConverter().orElse(DEFAULT_CONVERTER).stringify(value);
-               var m = getMatchPattern(pattern).matcher(v);
-               assertTrue(m.matches(), args.getMessage("Pattern didn't match. 
==> pattern: <{0}> but was: <{1}>", pattern, v));
+               var m = getGlobMatchPattern(pattern).matcher(v);
+               assertTrue(m.matches(), args.getMessage("Pattern didn''t match. 
==> pattern: <{0}> but was: <{1}>", pattern, v));
        }
 }
diff --git 
a/juneau-utest/src/test/java/org/apache/juneau/junit/BasicBeanConverter.java 
b/juneau-utest/src/test/java/org/apache/juneau/junit/BasicBeanConverter.java
index 5fbf6a042..423bc12cb 100644
--- a/juneau-utest/src/test/java/org/apache/juneau/junit/BasicBeanConverter.java
+++ b/juneau-utest/src/test/java/org/apache/juneau/junit/BasicBeanConverter.java
@@ -12,19 +12,12 @@
 // 
***************************************************************************************************************************
 package org.apache.juneau.junit;
 
-import static java.util.stream.Collectors.*;
-import static java.util.stream.StreamSupport.*;
-import static java.time.format.DateTimeFormatter.*;
-import static java.util.Collections.*;
 import static java.util.Optional.*;
 import static org.apache.juneau.junit.Utils.*;
+import static java.util.stream.Collectors.*;
 
 import java.io.*;
-
-import static java.util.Spliterators.*;
-
 import java.lang.reflect.*;
-import java.nio.file.*;
 import java.util.*;
 import java.util.concurrent.*;
 import java.util.function.*;
@@ -98,7 +91,7 @@ import java.util.stream.*;
  * <ul>
  *     <li><b>Optional:</b> Unwrapped to contained value or <jk>null</jk></li>
  *     <li><b>Supplier:</b> Called to get supplied value</li>
- *     <li><b>Future:</b> Extracts completed result or returns 
"&lt;pending&gt;" for incomplete futures</li>
+ *     <li><b>Future:</b> Extracts completed result or returns 
<js>"&lt;pending&gt;"</js> for incomplete futures (via {@link 
Swappers#futureSwapper()})</li>
  * </ul>
  *
  * <h5 class='section'>Configuration Settings:</h5>
@@ -107,6 +100,9 @@ import java.util.stream.*;
  *     <dt><code>nullValue</code></dt>
  *     <dd>String representation for null values (default: 
<js>"&lt;null&gt;"</js>)</dd>
  *
+ *     <dt><code>selfValue</code></dt>
+ *     <dd>Special property name that returns the object itself (default: 
<js>"&lt;self&gt;"</js>)</dd>
+ *
  *     <dt><code>emptyValue</code></dt>
  *     <dd>String representation for empty collections (default: 
<js>"&lt;empty&gt;"</js>)</dd>
  *
@@ -160,6 +156,20 @@ import java.util.stream.*;
  *     <jk>var</jk> <jv>orderCount</jv> = 
<jv>converter</jv>.getEntry(<jv>user</jv>, <js>"orders.length"</js>);
  * </p>
  *
+ * <p><b>Special Property Values:</b></p>
+ * <p class='bjava'>
+ *     <jc>// Use special property names</jc>
+ *     <jk>var</jk> <jv>userObj</jv> = 
<jv>converter</jv>.getEntry(<jv>user</jv>, <js>"&lt;self&gt;"</js>); <jc>// 
Returns the user object itself</jc>
+ *     <jk>var</jk> <jv>nullValue</jv> = 
<jv>converter</jv>.getEntry(<jv>user</jv>, <js>"&lt;null&gt;"</js>); <jc>// 
Returns null</jc>
+ *
+ *     <jc>// Custom self value</jc>
+ *     <jk>var</jk> <jv>customConverter</jv> = 
BasicBeanConverter.<jsm>builder</jsm>()
+ *             .defaultSettings()
+ *             .addSetting(<jsf>SETTING_selfValue</jsf>, <js>"this"</js>)
+ *             .build();
+ *     <jk>var</jk> <jv>selfRef</jv> = 
<jv>customConverter</jv>.getEntry(<jv>user</jv>, <js>"this"</js>); <jc>// 
Returns user object</jc>
+ * </p>
+ *
  * <h5 class='section'>Performance Characteristics:</h5>
  * <ul>
  *     <li><b>Handler Lookup:</b> O(1) average case via ConcurrentHashMap 
caching</li>
@@ -206,6 +216,7 @@ public class BasicBeanConverter implements BeanConverter {
 
        public static final String
                SETTING_nullValue = "nullValue",
+               SETTING_selfValue = "selfValue",
                SETTING_fieldSeparator = "fieldSeparator",
                SETTING_collectionPrefix = "collectionPrefix",
                SETTING_collectionSuffix = "collectionSuffix",
@@ -320,7 +331,15 @@ public class BasicBeanConverter implements BeanConverter {
 
                // Original logic for regular property access
                var pn = token.getValue();
-               var e = ofNullable(getProperty(o, pn.equals("<NULL>") ? null : 
pn)).orElse(null);
+               var selfValue = getSetting(SETTING_selfValue, "<self>");
+
+               // Handle special values
+               Object e;
+               if (pn.equals(selfValue)) {
+                       e = o; // Return the object itself
+               } else {
+                       e = ofNullable(getProperty(o, pn)).orElse(null);
+               }
                if (e == null || ! token.hasNested()) return stringify(e);
                return token.getNested().stream().map(x -> getNested(e, 
x)).map(this::stringify).collect(joining(",","{","}"));
        }
@@ -602,43 +621,33 @@ public class BasicBeanConverter implements BeanConverter {
                 */
                public Builder defaultSettings() {
                        addSetting(SETTING_nullValue, "<null>");
+                       addSetting(SETTING_selfValue, "<self>");
                        addSetting(SETTING_classNameFormat, "simple");
 
-                       addStringifier(Map.Entry.class, (bc, o) -> 
bc.stringify(o.getKey()) + bc.getSetting(SETTING_mapEntrySeparator, "=") + 
bc.stringify(o.getValue()));
-                       addStringifier(GregorianCalendar.class, (bc, o) ->  
o.toZonedDateTime().format(bc.getSetting(SETTING_calendarFormat, ISO_INSTANT)));
-                       addStringifier(Date.class, (bc, o) ->  
o.toInstant().toString());
-                       addStringifier(InputStream.class, (bc, o) ->  
stringify(o));
-                       addStringifier(byte[].class, (bc, o) ->  stringify(o));
-                       addStringifier(Reader.class, (bc, o) -> stringify(o));
-                       addStringifier(File.class, (bc, o) -> stringify(o));
-                       addStringifier(Enum.class, (bc, o) -> o.name());
-                       addStringifier(Class.class, (bc, o) -> stringify(bc, 
o));
-                       addStringifier(Constructor.class, (bc, o) -> 
stringify(bc, o));
-                       addStringifier(Method.class, (bc, o) -> stringify(bc, 
o));
-                       addStringifier(List.class, (bc, o) -> 
((List<?>)o).stream().map(bc::stringify).collect(joining(bc.getSetting(SETTING_fieldSeparator,
 ","), bc.getSetting(SETTING_collectionPrefix, "["), 
bc.getSetting(SETTING_collectionSuffix, "]"))));
-                       addStringifier(Map.class, (bc, o) -> 
((Map<?,?>)o).entrySet().stream().map(bc::stringify).collect(joining(bc.getSetting(SETTING_fieldSeparator,
 ","), bc.getSetting(SETTING_mapPrefix, "{"), bc.getSetting(SETTING_mapSuffix, 
"}"))));
-
-                       addListifier(Collection.class, (bc, o) -> new 
ArrayList<>(o));
-                       addListifier(Iterable.class, (bc, o) -> 
stream(((Iterable<Object>)o).spliterator(), false).toList());
-                       addListifier(Iterator.class, (bc, o) -> 
stream(spliteratorUnknownSize(o, 0), false).toList());
-                       addListifier(Enumeration.class, (bc, o) -> list(o));
-                       addListifier(Stream.class, (bc, o) -> o.toList());
-                       addListifier(Optional.class, (bc, o) -> o.isEmpty() ? 
emptyList() : singletonList(o.get()));
-                       addListifier(Map.class, (bc, o) -> new 
ArrayList<>(((Map<?,?>)o).entrySet()));
-
-                       addSwapper(Optional.class, (bc, o) -> o.orElse(null));
-                       addSwapper(Supplier.class, (bc, o) -> o.get());
-                       addSwapper(Future.class, (bc, future) -> {
-                               var f = (Future<?>)future;
-                               if (f.isDone() && !f.isCancelled()) {
-                                       try {
-                                               return f.get();
-                                       } catch (Exception e) {
-                                               return "<error: " + 
e.getMessage() + ">";
-                                       }
-                               }
-                               return f.isCancelled() ? "<cancelled>" : 
"<pending>";
-                       });
+                       addStringifier(Map.Entry.class, 
Stringifiers.mapEntryStringifier());
+                       addStringifier(GregorianCalendar.class, 
Stringifiers.calendarStringifier());
+                       addStringifier(Date.class, 
Stringifiers.dateStringifier());
+                       addStringifier(InputStream.class, 
Stringifiers.inputStreamStringifier());
+                       addStringifier(byte[].class, 
Stringifiers.byteArrayStringifier());
+                       addStringifier(Reader.class, 
Stringifiers.readerStringifier());
+                       addStringifier(File.class, 
Stringifiers.fileStringifier());
+                       addStringifier(Enum.class, 
Stringifiers.enumStringifier());
+                       addStringifier(Class.class, 
Stringifiers.classStringifier());
+                       addStringifier(Constructor.class, 
Stringifiers.constructorStringifier());
+                       addStringifier(Method.class, 
Stringifiers.methodStringifier());
+                       addStringifier(List.class, 
Stringifiers.listStringifier());
+                       addStringifier(Map.class, 
Stringifiers.mapStringifier());
+
+                       addListifier(Collection.class, 
Listifiers.collectionListifier());
+                       addListifier(Iterable.class, 
Listifiers.iterableListifier());
+                       addListifier(Iterator.class, 
Listifiers.iteratorListifier());
+                       addListifier(Enumeration.class, 
Listifiers.enumerationListifier());
+                       addListifier(Stream.class, 
Listifiers.streamListifier());
+                       addListifier(Map.class, Listifiers.mapListifier());
+
+                       addSwapper(Optional.class, Swappers.optionalSwapper());
+                       addSwapper(Supplier.class, Swappers.supplierSwapper());
+                       addSwapper(Future.class, Swappers.futureSwapper());
 
                        addPropertyExtractor(new 
PropertyExtractors.ObjectPropertyExtractor());
                        addPropertyExtractor(new 
PropertyExtractors.ListPropertyExtractor());
@@ -690,70 +699,4 @@ public class BasicBeanConverter implements BeanConverter {
                        this.function = function;
                }
        }
-
-       
//---------------------------------------------------------------------------------------------
-       // Helper methods.
-       
//---------------------------------------------------------------------------------------------
-
-       private static final char[] HEX = "0123456789ABCDEF".toCharArray();
-
-       private static String stringify(byte[] o) {
-               var sb = new StringBuilder(o.length * 2);
-               for (var element : o) {
-                       var v = element & 0xFF;
-                       sb.append(HEX[v >>> 4]).append(HEX[v & 0x0F]);
-               }
-               return sb.toString();
-       }
-
-       private static String stringify(InputStream o) {
-               return safe(() -> {
-                       try (var o2 = o) {
-                               var buff = new ByteArrayOutputStream(1024);
-                               var nRead = 0;
-                               var b = new byte[1024];
-                               while ((nRead = o2.read(b, 0, b.length)) != -1)
-                                       buff.write(b, 0, nRead);
-                               buff.flush();
-                               return stringify(buff.toByteArray());
-                       }
-               });
-       }
-
-       private static String stringify(Reader o) {
-               return safe(() -> {
-                       try (var o2 = o) {
-                               var sb = new StringBuilder();
-                               var buf = new char[1024];
-                               var i = 0;
-                               while ((i = o2.read(buf)) != -1)
-                                       sb.append(buf, 0, i);
-                               return sb.toString();
-                       }
-               });
-       }
-
-       private static String stringify(File o) {
-               return safe(()->stringify(Files.newBufferedReader(o.toPath())));
-       }
-
-       private static String stringify(BeanConverter bc, Class<?> o) {
-               return switch(bc.getSetting(SETTING_classNameFormat, 
"default")) {
-                       case "simple" -> o.getSimpleName();
-                       case "canonical" -> o.getCanonicalName();
-                       default -> o.getName();
-               };
-       }
-
-       private static String stringify(BeanConverter bc, Constructor o) {
-               return new StringBuilder().append(stringify(bc, 
o.getDeclaringClass())).append('(').append(stringify(bc, 
o.getParameterTypes())).append(')').toString();
-       }
-
-       private static String stringify(BeanConverter bc, Method o) {
-               return new 
StringBuilder().append(o.getName()).append('(').append(stringify(bc, 
o.getParameterTypes())).append(')').toString();
-       }
-
-       private static String stringify(BeanConverter bc, Class[] o) {
-               return Arrays.stream(o).map(x -> stringify(bc, 
x)).collect(joining(","));
-       }
 }
diff --git 
a/juneau-utest/src/test/java/org/apache/juneau/junit/BasicBeanConverter_Test.java
 
b/juneau-utest/src/test/java/org/apache/juneau/junit/BasicBeanConverter_Test.java
index f48f15620..c264c621b 100644
--- 
a/juneau-utest/src/test/java/org/apache/juneau/junit/BasicBeanConverter_Test.java
+++ 
b/juneau-utest/src/test/java/org/apache/juneau/junit/BasicBeanConverter_Test.java
@@ -27,7 +27,7 @@ import org.junit.jupiter.api.*;
  * Unit tests for BasicBeanConverter.
  */
 @DisplayName("BasicBeanConverter")
-public class BasicBeanConverter_Test extends TestBase {
+class BasicBeanConverter_Test extends TestBase {
 
        
//------------------------------------------------------------------------------------------------------------------
        // Builder Tests
@@ -35,7 +35,7 @@ public class BasicBeanConverter_Test extends TestBase {
 
        @Nested
        @DisplayName("Builder")
-       class BuilderTest {
+       class a_BuilderTest {
 
                @Test
                @DisplayName("a01_builder() creates new Builder instance")
@@ -146,7 +146,7 @@ public class BasicBeanConverter_Test extends TestBase {
 
        @Nested
        @DisplayName("Core Functionality")
-       class CoreFunctionalityTest {
+       class b_CoreFunctionalityTest {
 
                private BasicBeanConverter converter;
 
@@ -266,7 +266,7 @@ public class BasicBeanConverter_Test extends TestBase {
 
        @Nested
        @DisplayName("Property Access")
-       class PropertyAccessTest {
+       class c_PropertyAccessTest {
 
                private BasicBeanConverter converter;
 
@@ -306,7 +306,7 @@ public class BasicBeanConverter_Test extends TestBase {
 
        @Nested
        @DisplayName("Settings")
-       class SettingsTest {
+       class d_SettingsTest {
 
                @Test
                @DisplayName("d01_nullValue setting changes null 
representation")
@@ -390,7 +390,7 @@ public class BasicBeanConverter_Test extends TestBase {
 
        @Nested
        @DisplayName("Error Handling")
-       class ErrorHandlingTest {
+       class e_ErrorHandlingTest {
 
                private BasicBeanConverter converter;
 
@@ -425,7 +425,7 @@ public class BasicBeanConverter_Test extends TestBase {
 
                public String getName() { return name; }
                public int getAge() { return age; }
-               public void setName(String name) { this.name = name; }
-               public void setAge(int age) { this.age = age; }
+               void setName(String name) { this.name = name; }
+               void setAge(int age) { this.age = age; }
        }
 }
diff --git a/juneau-utest/src/test/java/org/apache/juneau/junit/Listifiers.java 
b/juneau-utest/src/test/java/org/apache/juneau/junit/Listifiers.java
new file mode 100644
index 000000000..bc4def86b
--- /dev/null
+++ b/juneau-utest/src/test/java/org/apache/juneau/junit/Listifiers.java
@@ -0,0 +1,346 @@
+// 
***************************************************************************************************************************
+// * 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.juneau.junit;
+
+import static java.util.Collections.*;
+import static java.util.stream.StreamSupport.*;
+import static java.util.Spliterators.*;
+
+import java.util.*;
+import java.util.stream.*;
+
+/**
+ * Collection of standard listifier implementations for the Bean-Centric 
Testing framework.
+ *
+ * <p>This class provides built-in list conversion strategies that handle 
common Java collection
+ * and iterable types. These listifiers are automatically registered when using
+ * {@link BasicBeanConverter.Builder#defaultSettings()}.</p>
+ *
+ * <h5 class='section'>Purpose:</h5>
+ * <p>Listifiers convert various collection-like objects to {@link List} 
instances for uniform
+ * processing in BCT assertions. This enables consistent iteration and element 
access across
+ * different data structures like arrays, iterators, streams, and custom 
collections.</p>
+ *
+ * <h5 class='section'>Built-in Listifiers:</h5>
+ * <ul>
+ *     <li><b>{@link #collectionListifier()}</b> - Converts {@link Collection} 
objects to {@link ArrayList}</li>
+ *     <li><b>{@link #iterableListifier()}</b> - Converts {@link Iterable} 
objects using streams</li>
+ *     <li><b>{@link #iteratorListifier()}</b> - Converts {@link Iterator} 
objects to lists (consumes iterator)</li>
+ *     <li><b>{@link #enumerationListifier()}</b> - Converts {@link 
Enumeration} objects to lists</li>
+ *     <li><b>{@link #streamListifier()}</b> - Converts {@link Stream} objects 
to lists (terminates stream)</li>
+ *     <li><b>{@link #optionalListifier()}</b> - Converts {@link Optional} to 
empty or single-element list</li>
+ *     <li><b>{@link #mapListifier()}</b> - Converts {@link Map} to list of 
{@link Map.Entry} objects</li>
+ * </ul>
+ *
+ * <h5 class='section'>Usage Example:</h5>
+ * <p class='bjava'>
+ *     <jc>// Register listifiers using builder</jc>
+ *     <jk>var</jk> <jv>converter</jv> = 
BasicBeanConverter.<jsm>builder</jsm>()
+ *             .defaultSettings()
+ *             .addListifier(Collection.<jk>class</jk>, 
Listifiers.<jsm>collectionListifier</jsm>())
+ *             .addListifier(Stream.<jk>class</jk>, 
Listifiers.<jsm>streamListifier</jsm>())
+ *             .build();
+ * </p>
+ *
+ * <h5 class='section'>Iterator Consumption:</h5>
+ * <p><b>Warning:</b> Some listifiers consume their input objects during 
conversion:</p>
+ * <ul>
+ *     <li><b>{@link Iterator}:</b> Elements are consumed and iterator becomes 
exhausted</li>
+ *     <li><b>{@link Enumeration}:</b> Elements are consumed and enumeration 
becomes exhausted</li>
+ *     <li><b>{@link Stream}:</b> Stream is terminated and cannot be 
reused</li>
+ * </ul>
+ *
+ * <h5 class='section'>Custom Listifier Development:</h5>
+ * <p>When creating custom listifiers, follow these patterns:</p>
+ * <ul>
+ *     <li><b>Null Safety:</b> Handle <jk>null</jk> inputs gracefully</li>
+ *     <li><b>Immutability:</b> Return new lists rather than modifying 
inputs</li>
+ *     <li><b>Type Safety:</b> Ensure proper generic type handling</li>
+ *     <li><b>Performance:</b> Consider memory usage for large collections</li>
+ * </ul>
+ *
+ * @see Listifier
+ * @see BasicBeanConverter.Builder#addListifier(Class, Listifier)
+ * @see BasicBeanConverter.Builder#defaultSettings()
+ */
+@SuppressWarnings({"unchecked","rawtypes"})
+public class Listifiers {
+
+       /**
+        * Constructor.
+        */
+       private Listifiers() {}
+
+       /**
+        * Returns a listifier for {@link Collection} objects that converts 
them to {@link ArrayList}.
+        *
+        * <p>This listifier creates a new ArrayList containing all elements 
from the source collection.
+        * It works with any Collection subtype including List, Set, Queue, and 
Deque.</p>
+        *
+        * <h5 class='section'>Behavior:</h5>
+        * <ul>
+        *      <li><b>Non-empty collections:</b> Returns new ArrayList with 
all elements in iteration order</li>
+        *      <li><b>Empty collections:</b> Returns new empty ArrayList</li>
+        *      <li><b>Preserves order:</b> Maintains the iteration order of 
the source collection</li>
+        * </ul>
+        *
+        * <h5 class='section'>Usage Examples:</h5>
+        * <p class='bjava'>
+        *      <jc>// Test with different collection types</jc>
+        *      <jk>var</jk> <jv>list</jv> = List.<jsm>of</jsm>(<js>"a"</js>, 
<js>"b"</js>, <js>"c"</js>);
+        *      <jsm>assertList</jsm>(<jv>list</jv>, <js>"a"</js>, 
<js>"b"</js>, <js>"c"</js>);
+        *
+        *      <jk>var</jk> <jv>set</jv> = Set.<jsm>of</jsm>(<js>"x"</js>, 
<js>"y"</js>);
+        *      <jsm>assertList</jsm>(<jv>set</jv>, <js>"x"</js>, 
<js>"y"</js>); <jc>// Order may vary</jc>
+        *
+        *      <jk>var</jk> <jv>queue</jv> = <jk>new</jk> 
LinkedList&lt;&gt;(Arrays.<jsm>asList</jsm>(<js>"first"</js>, 
<js>"second"</js>));
+        *      <jsm>assertList</jsm>(<jv>queue</jv>, <js>"first"</js>, 
<js>"second"</js>);
+        * </p>
+        *
+        * <h5 class='section'>Performance:</h5>
+        * <p>This listifier creates a new ArrayList and copies all elements, 
so it has O(n) time
+        * and space complexity. For large collections, consider the memory 
implications.</p>
+        *
+        * @return A {@link Listifier} for {@link Collection} objects
+        * @see Collection
+        * @see ArrayList
+        */
+       public static Listifier<Collection> collectionListifier() {
+               return (bc, collection) -> new ArrayList<>(collection);
+       }
+
+       /**
+        * Returns a listifier for {@link Iterable} objects that converts them 
using streams.
+        *
+        * <p>This listifier handles any Iterable implementation by creating a 
stream from its
+        * spliterator and collecting elements to a list. It works with custom 
iterables and
+        * collection types not covered by more specific listifiers.</p>
+        *
+        * <h5 class='section'>Behavior:</h5>
+        * <ul>
+        *      <li><b>Standard iterables:</b> Converts elements to list 
maintaining iteration order</li>
+        *      <li><b>Custom iterables:</b> Works with any object implementing 
Iterable interface</li>
+        *      <li><b>Large iterables:</b> Processes all elements regardless 
of size</li>
+        * </ul>
+        *
+        * <h5 class='section'>Usage Examples:</h5>
+        * <p class='bjava'>
+        *      <jc>// Test with custom iterable</jc>
+        *      <jk>var</jk> <jv>range</jv> = 
IntStream.<jsm>range</jsm>(<jv>1</jv>, 
<jv>4</jv>).<jsm>boxed</jsm>().<jsm>collect</jsm>(toList());
+        *      <jsm>assertList</jsm>(<jv>range</jv>, <jv>1</jv>, <jv>2</jv>, 
<jv>3</jv>);
+        *
+        *      <jc>// Test with custom Iterable implementation</jc>
+        *      <jk>var</jk> <jv>fibonacci</jv> = <jk>new</jk> 
FibonacciIterable(<jv>5</jv>);
+        *      <jsm>assertList</jsm>(<jv>fibonacci</jv>, <jv>1</jv>, 
<jv>1</jv>, <jv>2</jv>, <jv>3</jv>, <jv>5</jv>);
+        * </p>
+        *
+        * @return A {@link Listifier} for {@link Iterable} objects
+        * @see Iterable
+        */
+       public static Listifier<Iterable> iterableListifier() {
+               return (bc, iterable) -> stream(iterable.spliterator(), 
false).toList();
+       }
+
+       /**
+        * Returns a listifier for {@link Iterator} objects that converts them 
to lists.
+        *
+        * <p><b>Warning:</b> This listifier consumes the iterator during 
conversion. After listification,
+        * the iterator will be exhausted and cannot be used again.</p>
+        *
+        * <h5 class='section'>Behavior:</h5>
+        * <ul>
+        *      <li><b>Element extraction:</b> Consumes all remaining elements 
from the iterator</li>
+        *      <li><b>Order preservation:</b> Maintains the iterator's order 
in the resulting list</li>
+        *      <li><b>Iterator exhaustion:</b> The iterator becomes unusable 
after conversion</li>
+        * </ul>
+        *
+        * <h5 class='section'>Usage Examples:</h5>
+        * <p class='bjava'>
+        *      <jc>// Test with list iterator</jc>
+        *      <jk>var</jk> <jv>list</jv> = List.<jsm>of</jsm>(<js>"a"</js>, 
<js>"b"</js>, <js>"c"</js>);
+        *      <jk>var</jk> <jv>iterator</jv> = <jv>list</jv>.iterator();
+        *      <jsm>assertList</jsm>(<jv>iterator</jv>, <js>"a"</js>, 
<js>"b"</js>, <js>"c"</js>);
+        *      <jc>// iterator is now exhausted</jc>
+        *
+        *      <jc>// Test with custom iterator</jc>
+        *      <jk>var</jk> <jv>numbers</jv> = 
IntStream.<jsm>range</jsm>(<jv>1</jv>, <jv>4</jv>).iterator();
+        *      <jsm>assertList</jsm>(<jv>numbers</jv>, <jv>1</jv>, <jv>2</jv>, 
<jv>3</jv>);
+        * </p>
+        *
+        * <h5 class='section'>Important Notes:</h5>
+        * <ul>
+        *      <li><b>One-time use:</b> The iterator becomes exhausted and 
unusable after conversion</li>
+        *      <li><b>Side effects:</b> Any side effects in the iterator's 
next() method will occur</li>
+        *      <li><b>Exception handling:</b> Exceptions from the iterator are 
propagated</li>
+        * </ul>
+        *
+        * @return A {@link Listifier} for {@link Iterator} objects
+        * @see Iterator
+        */
+       public static Listifier<Iterator> iteratorListifier() {
+               return (bc, iterator) -> 
stream(spliteratorUnknownSize(iterator, 0), false).toList();
+       }
+
+       /**
+        * Returns a listifier for {@link Enumeration} objects that converts 
them to lists.
+        *
+        * <p><b>Warning:</b> This listifier consumes the enumeration during 
conversion. After listification,
+        * the enumeration will be exhausted and cannot be used again.</p>
+        *
+        * <h5 class='section'>Behavior:</h5>
+        * <ul>
+        *      <li><b>Element extraction:</b> Consumes all remaining elements 
from the enumeration</li>
+        *      <li><b>Order preservation:</b> Maintains the enumeration's 
order in the resulting list</li>
+        *      <li><b>Enumeration exhaustion:</b> The enumeration becomes 
unusable after conversion</li>
+        * </ul>
+        *
+        * <h5 class='section'>Usage Examples:</h5>
+        * <p class='bjava'>
+        *      <jc>// Test with Vector enumeration</jc>
+        *      <jk>var</jk> <jv>vector</jv> = <jk>new</jk> 
Vector&lt;&gt;(List.<jsm>of</jsm>(<js>"x"</js>, <js>"y"</js>, <js>"z"</js>));
+        *      <jk>var</jk> <jv>enumeration</jv> = <jv>vector</jv>.elements();
+        *      <jsm>assertList</jsm>(<jv>enumeration</jv>, <js>"x"</js>, 
<js>"y"</js>, <js>"z"</js>);
+        *
+        *      <jc>// Test with Hashtable enumeration</jc>
+        *      <jk>var</jk> <jv>table</jv> = <jk>new</jk> 
Hashtable&lt;&gt;(Map.<jsm>of</jsm>(<js>"key1"</js>, <js>"value1"</js>));
+        *      <jk>var</jk> <jv>keys</jv> = <jv>table</jv>.keys();
+        *      <jsm>assertList</jsm>(<jv>keys</jv>, <js>"key1"</js>);
+        * </p>
+        *
+        * @return A {@link Listifier} for {@link Enumeration} objects
+        * @see Enumeration
+        */
+       public static Listifier<Enumeration> enumerationListifier() {
+               return (bc, enumeration) -> list(enumeration);
+       }
+
+       /**
+        * Returns a listifier for {@link Stream} objects that converts them to 
lists.
+        *
+        * <p><b>Warning:</b> This listifier terminates the stream during 
conversion. After listification,
+        * the stream is closed and cannot be used again.</p>
+        *
+        * <h5 class='section'>Behavior:</h5>
+        * <ul>
+        *      <li><b>Stream termination:</b> Calls toList() to collect all 
stream elements</li>
+        *      <li><b>Order preservation:</b> Maintains stream order in the 
resulting list</li>
+        *      <li><b>Stream closure:</b> The stream becomes unusable after 
conversion</li>
+        * </ul>
+        *
+        * <h5 class='section'>Usage Examples:</h5>
+        * <p class='bjava'>
+        *      <jc>// Test with filtered stream</jc>
+        *      <jk>var</jk> <jv>numbers</jv> = 
IntStream.<jsm>range</jsm>(<jv>1</jv>, <jv>10</jv>)
+        *              .filter(<jv>n</jv> -&gt; <jv>n</jv> % <jv>2</jv> == 
<jv>0</jv>)
+        *              .boxed();
+        *      <jsm>assertList</jsm>(<jv>numbers</jv>, <jv>2</jv>, <jv>4</jv>, 
<jv>6</jv>, <jv>8</jv>);
+        *
+        *      <jc>// Test with mapped stream</jc>
+        *      <jk>var</jk> <jv>words</jv> = 
Stream.<jsm>of</jsm>(<js>"hello"</js>, <js>"world"</js>)
+        *              .map(String::toUpperCase);
+        *      <jsm>assertList</jsm>(<jv>words</jv>, <js>"HELLO"</js>, 
<js>"WORLD"</js>);
+        * </p>
+        *
+        * <h5 class='section'>Important Notes:</h5>
+        * <ul>
+        *      <li><b>One-time use:</b> The stream is terminated and cannot be 
reused</li>
+        *      <li><b>Lazy evaluation:</b> Stream operations are executed 
during listification</li>
+        *      <li><b>Exception handling:</b> Stream operation exceptions are 
propagated</li>
+        * </ul>
+        *
+        * @return A {@link Listifier} for {@link Stream} objects
+        * @see Stream
+        */
+       public static Listifier<Stream> streamListifier() {
+               return (bc, stream) -> stream.toList();
+       }
+
+       /**
+        * Returns a listifier for {@link Optional} objects that converts them 
to single-element or empty lists.
+        *
+        * <p>This listifier enables Optional values to be processed as lists 
in BCT assertions,
+        * making it easy to test Optional-wrapped collections and values 
uniformly.</p>
+        *
+        * <h5 class='section'>Behavior:</h5>
+        * <ul>
+        *      <li><b>Present value:</b> Returns single-element list 
containing the Optional's value</li>
+        *      <li><b>Empty Optional:</b> Returns empty list</li>
+        *      <li><b>Type preservation:</b> The list element type matches the 
Optional's type parameter</li>
+        * </ul>
+        *
+        * <h5 class='section'>Usage Examples:</h5>
+        * <p class='bjava'>
+        *      <jc>// Test Optional with value</jc>
+        *      <jk>var</jk> <jv>optional</jv> = 
Optional.<jsm>of</jsm>(<js>"Hello"</js>);
+        *      <jsm>assertList</jsm>(<jv>optional</jv>, <js>"Hello"</js>);
+        *
+        *      <jc>// Test empty Optional</jc>
+        *      <jk>var</jk> <jv>empty</jv> = Optional.<jsm>empty</jsm>();
+        *      <jsm>assertEmpty</jsm>(<jv>empty</jv>);
+        *
+        *      <jc>// Test Optional in collection context</jc>
+        *      <jk>var</jk> <jv>user</jv> = <jk>new</jk> 
User().setNickname(Optional.<jsm>of</jsm>(<js>"Johnny"</js>));
+        *      <jsm>assertList</jsm>(<jv>user</jv>, <js>"nickname"</js>, 
<js>"Johnny"</js>);
+        * </p>
+        *
+        * <h5 class='section'>Integration:</h5>
+        * <p>This listifier is particularly useful when working with APIs that 
return Optional
+        * collections or when testing Optional values in a list context.</p>
+        *
+        * @return A {@link Listifier} for {@link Optional} objects
+        * @see Optional
+        */
+       public static Listifier<Optional> optionalListifier() {
+               return (bc, optional) -> optional.isEmpty() ? emptyList() : 
singletonList(optional.get());
+       }
+
+       /**
+        * Returns a listifier for {@link Map} objects that converts them to 
lists of {@link Map.Entry} objects.
+        *
+        * <p>This listifier enables maps to be processed as lists in BCT 
assertions, making it easy
+        * to test map contents using list-based assertion methods.</p>
+        *
+        * <h5 class='section'>Behavior:</h5>
+        * <ul>
+        *      <li><b>Entry conversion:</b> Each key-value pair becomes a 
Map.Entry in the list</li>
+        *      <li><b>Order preservation:</b> Maintains the map's iteration 
order</li>
+        *      <li><b>Empty maps:</b> Returns empty list for empty maps</li>
+        * </ul>
+        *
+        * <h5 class='section'>Usage Examples:</h5>
+        * <p class='bjava'>
+        *      <jc>// Test map contents</jc>
+        *      <jk>var</jk> <jv>map</jv> = Map.<jsm>of</jsm>(<js>"key1"</js>, 
<js>"value1"</js>, <js>"key2"</js>, <js>"value2"</js>);
+        *      <jsm>assertSize</jsm>(<jv>map</jv>, <jv>2</jv>);
+        *
+        *      <jc>// Test empty map</jc>
+        *      <jk>var</jk> <jv>emptyMap</jv> = Map.<jsm>of</jsm>();
+        *      <jsm>assertEmpty</jsm>(<jv>emptyMap</jv>);
+        *
+        *      <jc>// Test map in object property</jc>
+        *      <jk>var</jk> <jv>config</jv> = <jk>new</jk> 
Configuration().setProperties(<jv>map</jv>);
+        *      <jsm>assertSize</jsm>(<jv>config</jv>, <js>"properties"</js>, 
<jv>2</jv>);
+        * </p>
+        *
+        * <h5 class='section'>Entry Processing:</h5>
+        * <p>The resulting Map.Entry objects can be further processed by other 
parts of the
+        * conversion system, typically being stringified to 
<js>"key=value"</js> format.</p>
+        *
+        * @return A {@link Listifier} for {@link Map} objects
+        * @see Map
+        * @see Map.Entry
+        */
+       public static Listifier<Map> mapListifier() {
+               return (bc, map) -> new ArrayList<>(map.entrySet());
+       }
+}
diff --git 
a/juneau-utest/src/test/java/org/apache/juneau/junit/Listifiers_Test.java 
b/juneau-utest/src/test/java/org/apache/juneau/junit/Listifiers_Test.java
new file mode 100644
index 000000000..6c4260d1f
--- /dev/null
+++ b/juneau-utest/src/test/java/org/apache/juneau/junit/Listifiers_Test.java
@@ -0,0 +1,411 @@
+// 
***************************************************************************************************************************
+// * 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.juneau.junit;
+
+import static org.apache.juneau.junit.Assertions2.*;
+import static org.junit.jupiter.api.Assertions.*;
+
+import java.util.*;
+import java.util.stream.*;
+
+import org.junit.jupiter.api.*;
+
+/**
+ * Unit tests for {@link Listifiers}.
+ */
+class Listifiers_Test extends TestBase {
+
+       @Nested
+       class a_CollectionListifier {
+
+               @Test
+               void a01_listifyList() {
+                       var listifier = Listifiers.collectionListifier();
+                       var input = List.of("a", "b", "c");
+                       var result = listifier.apply(null, input);
+
+                       assertNotSame(input, result); // Should create new list
+                       assertList(result, "a", "b", "c");
+               }
+
+               @Test
+               void a02_listifySet() {
+                       var listifier = Listifiers.collectionListifier();
+                       var input = Set.of("x", "y", "z");
+                       var result = listifier.apply(null, input);
+
+                       assertEquals(3, result.size());
+                       assertTrue(result.contains("x"));
+                       assertTrue(result.contains("y"));
+                       assertTrue(result.contains("z"));
+               }
+
+               @Test
+               void a03_listifyQueue() {
+                       var listifier = Listifiers.collectionListifier();
+                       var input = new LinkedList<>(List.of("first", 
"second"));
+                       var result = listifier.apply(null, input);
+
+                       assertList(result, "first", "second");
+               }
+
+               @Test
+               void a04_listifyEmptyCollection() {
+                       var listifier = Listifiers.collectionListifier();
+                       var input = List.of();
+                       var result = listifier.apply(null, input);
+
+                       assertTrue(result.isEmpty());
+               }
+
+               @Test
+               void a05_listifyWithConverter() {
+                       var converter = 
BasicBeanConverter.builder().defaultSettings().build();
+                       var listifier = Listifiers.collectionListifier();
+                       var input = List.of(1, 2, 3);
+                       var result = listifier.apply(converter, input);
+
+                       assertList(result, 1, 2, 3);
+               }
+       }
+
+       @Nested
+       class b_IterableListifier {
+
+               @Test
+               void b01_listifyIterable() {
+                       var listifier = Listifiers.iterableListifier();
+                       Iterable<String> input = List.of("a", "b", "c");
+                       var result = listifier.apply(null, input);
+
+                       assertList(result, "a", "b", "c");
+               }
+
+               @Test
+               void b02_listifyCustomIterable() {
+                       var listifier = Listifiers.iterableListifier();
+                       // Create a simple custom iterable
+                       Iterable<Integer> input = () -> IntStream.range(1, 
4).iterator();
+                       var result = listifier.apply(null, input);
+
+                       assertList(result, 1, 2, 3);
+               }
+
+               @Test
+               void b03_listifyEmptyIterable() {
+                       var listifier = Listifiers.iterableListifier();
+                       Iterable<String> input = List.of();
+                       var result = listifier.apply(null, input);
+
+                       assertTrue(result.isEmpty());
+               }
+
+               @Test
+               void b04_listifyWithConverter() {
+                       var converter = 
BasicBeanConverter.builder().defaultSettings().build();
+                       var listifier = Listifiers.iterableListifier();
+                       Iterable<String> input = Set.of("x", "y");
+                       var result = listifier.apply(converter, input);
+
+                       assertEquals(2, result.size());
+                       assertTrue(result.contains("x"));
+                       assertTrue(result.contains("y"));
+               }
+       }
+
+       @Nested
+       class c_IteratorListifier {
+
+               @Test
+               void c01_listifyIterator() {
+                       var listifier = Listifiers.iteratorListifier();
+                       var input = List.of("a", "b", "c").iterator();
+                       var result = listifier.apply(null, input);
+
+                       assertList(result, "a", "b", "c");
+                       // Iterator should be exhausted
+                       assertFalse(input.hasNext());
+               }
+
+               @Test
+               void c02_listifyEmptyIterator() {
+                       var listifier = Listifiers.iteratorListifier();
+                       var input = List.of().iterator();
+                       var result = listifier.apply(null, input);
+
+                       assertTrue(result.isEmpty());
+               }
+
+               @Test
+               void c03_listifyLargeIterator() {
+                       var listifier = Listifiers.iteratorListifier();
+                       var input = IntStream.range(0, 1000).iterator();
+                       var result = listifier.apply(null, input);
+
+                       assertEquals(1000, result.size());
+                       assertEquals(0, result.get(0));
+                       assertEquals(999, result.get(999));
+               }
+
+               @Test
+               void c04_listifyWithConverter() {
+                       var converter = 
BasicBeanConverter.builder().defaultSettings().build();
+                       var listifier = Listifiers.iteratorListifier();
+                       var input = List.of("x", "y", "z").iterator();
+                       var result = listifier.apply(converter, input);
+
+                       assertList(result, "x", "y", "z");
+               }
+       }
+
+       @Nested
+       class d_EnumerationListifier {
+
+               @Test
+               void d01_listifyEnumeration() {
+                       var listifier = Listifiers.enumerationListifier();
+                       var vector = new Vector<>(List.of("a", "b", "c"));
+                       var input = vector.elements();
+                       var result = listifier.apply(null, input);
+
+                       assertList(result, "a", "b", "c");
+                       // Enumeration should be exhausted
+                       assertFalse(input.hasMoreElements());
+               }
+
+               @Test
+               void d02_listifyEmptyEnumeration() {
+                       var listifier = Listifiers.enumerationListifier();
+                       var vector = new Vector<String>();
+                       var input = vector.elements();
+                       var result = listifier.apply(null, input);
+
+                       assertTrue(result.isEmpty());
+               }
+
+               @Test
+               void d03_listifyHashtableKeys() {
+                       var listifier = Listifiers.enumerationListifier();
+                       var hashtable = new Hashtable<String, String>();
+                       hashtable.put("key1", "value1");
+                       hashtable.put("key2", "value2");
+                       var input = hashtable.keys();
+                       var result = listifier.apply(null, input);
+
+                       assertEquals(2, result.size());
+                       assertTrue(result.contains("key1"));
+                       assertTrue(result.contains("key2"));
+               }
+
+               @Test
+               void d04_listifyWithConverter() {
+                       var converter = 
BasicBeanConverter.builder().defaultSettings().build();
+                       var listifier = Listifiers.enumerationListifier();
+                       var vector = new Vector<>(List.of(1, 2, 3));
+                       var input = vector.elements();
+                       var result = listifier.apply(converter, input);
+
+                       assertList(result, 1, 2, 3);
+               }
+       }
+
+       @Nested
+       class e_StreamListifier {
+
+               @Test
+               void e01_listifyStream() {
+                       var listifier = Listifiers.streamListifier();
+                       var input = Stream.of("a", "b", "c");
+                       var result = listifier.apply(null, input);
+
+                       assertList(result, "a", "b", "c");
+               }
+
+               @Test
+               void e02_listifyEmptyStream() {
+                       var listifier = Listifiers.streamListifier();
+                       var input = Stream.empty();
+                       var result = listifier.apply(null, input);
+
+                       assertTrue(result.isEmpty());
+               }
+
+               @Test
+               void e03_listifyFilteredStream() {
+                       var listifier = Listifiers.streamListifier();
+                       var input = IntStream.range(1, 10)
+                               .filter(n -> n % 2 == 0)
+                               .boxed();
+                       var result = listifier.apply(null, input);
+
+                       assertList(result, 2, 4, 6, 8);
+               }
+
+               @Test
+               void e04_listifyMappedStream() {
+                       var listifier = Listifiers.streamListifier();
+                       var input = Stream.of("hello", "world")
+                               .map(String::toUpperCase);
+                       var result = listifier.apply(null, input);
+
+                       assertList(result, "HELLO", "WORLD");
+               }
+
+               @Test
+               void e05_listifyWithConverter() {
+                       var converter = 
BasicBeanConverter.builder().defaultSettings().build();
+                       var listifier = Listifiers.streamListifier();
+                       var input = Stream.of(1, 2, 3);
+                       var result = listifier.apply(converter, input);
+
+                       assertList(result, 1, 2, 3);
+               }
+       }
+
+       @Nested
+       class f_OptionalListifier {
+
+               @Test
+               void f01_listifyPresentOptional() {
+                       var listifier = Listifiers.optionalListifier();
+                       var input = Optional.of("hello");
+                       var result = listifier.apply(null, input);
+
+                       assertList(result, "hello");
+               }
+
+               @Test
+               void f02_listifyEmptyOptional() {
+                       var listifier = Listifiers.optionalListifier();
+                       var input = Optional.empty();
+                       var result = listifier.apply(null, input);
+
+                       assertTrue(result.isEmpty());
+               }
+
+               @Test
+               void f03_listifyOptionalWithNull() {
+                       var listifier = Listifiers.optionalListifier();
+                       var input = Optional.ofNullable(null);
+                       var result = listifier.apply(null, input);
+
+                       assertTrue(result.isEmpty());
+               }
+
+               @Test
+               void f04_listifyOptionalWithComplexObject() {
+                       var listifier = Listifiers.optionalListifier();
+                       var complexObject = Map.of("key", "value");
+                       var input = Optional.of(complexObject);
+                       var result = listifier.apply(null, input);
+
+                       assertEquals(1, result.size());
+                       assertSame(complexObject, result.get(0));
+               }
+
+               @Test
+               void f05_listifyWithConverter() {
+                       var converter = 
BasicBeanConverter.builder().defaultSettings().build();
+                       var listifier = Listifiers.optionalListifier();
+                       var input = Optional.of(42);
+                       var result = listifier.apply(converter, input);
+
+                       assertList(result, 42);
+               }
+       }
+
+       @Nested
+       class g_MapListifier {
+
+               @Test
+               void g01_listifyMap() {
+                       var listifier = Listifiers.mapListifier();
+                       var input = Map.of("key1", "value1", "key2", "value2");
+                       var result = listifier.apply(null, input);
+
+                       assertEquals(2, result.size());
+                       // Result should contain Map.Entry objects
+                       assertTrue(result.stream().allMatch(obj -> obj 
instanceof Map.Entry));
+               }
+
+               @Test
+               void g02_listifyEmptyMap() {
+                       var listifier = Listifiers.mapListifier();
+                       var input = Map.of();
+                       var result = listifier.apply(null, input);
+
+                       assertTrue(result.isEmpty());
+               }
+
+               @Test
+               void g03_listifyMapWithNullValues() {
+                       var listifier = Listifiers.mapListifier();
+                       var input = new HashMap<String, String>();
+                       input.put("key1", "value1");
+                       input.put("key2", null);
+                       var result = listifier.apply(null, input);
+
+                       assertEquals(2, result.size());
+                       // Check that we have the expected entries
+                       var hasKey1Entry = result.stream()
+                               .filter(obj -> obj instanceof Map.Entry)
+                               .map(obj -> (Map.Entry<?, ?>) obj)
+                               .anyMatch(entry -> 
"key1".equals(entry.getKey()) && "value1".equals(entry.getValue()));
+                       assertTrue(hasKey1Entry);
+
+                       var hasKey2Entry = result.stream()
+                               .filter(obj -> obj instanceof Map.Entry)
+                               .map(obj -> (Map.Entry<?, ?>) obj)
+                               .anyMatch(entry -> 
"key2".equals(entry.getKey()) && entry.getValue() == null);
+                       assertTrue(hasKey2Entry);
+               }
+
+               @Test
+               void g04_listifyWithConverter() {
+                       var converter = 
BasicBeanConverter.builder().defaultSettings().build();
+                       var listifier = Listifiers.mapListifier();
+                       var input = Map.of("a", 1, "b", 2);
+                       var result = listifier.apply(converter, input);
+
+                       assertEquals(2, result.size());
+                       assertTrue(result.stream().allMatch(obj -> obj 
instanceof Map.Entry));
+               }
+       }
+
+       @Nested
+       class h_Integration {
+
+               @Test
+               void h01_useInBasicBeanConverter() {
+                       // Test various listifiable objects
+                       assertList(List.of("a", "b"), "a", "b");
+                       assertList(new LinkedHashSet<>(Arrays.asList("x", 
"y")), "x", "y");
+                       assertSize(3, Stream.of(1, 2, 3));
+                       assertEmpty(Optional.empty());
+               }
+
+               @Test
+               void h02_customListifierRegistration() {
+                       // Test that custom registration works
+                       assertList(List.of("custom"), "custom");
+               }
+
+               @Test
+               void h03_listifierChaining() {
+                       // Test that listified objects can be processed by 
other listifiers
+                       // Stream of optionals
+                       var streamOfOptionals = Stream.of(Optional.of("a"), 
Optional.empty(), Optional.of("b"));
+                       assertSize(3, streamOfOptionals);
+               }
+       }
+}
diff --git 
a/juneau-utest/src/test/java/org/apache/juneau/junit/PropertyExtractors.java 
b/juneau-utest/src/test/java/org/apache/juneau/junit/PropertyExtractors.java
index ca8127af9..189611128 100644
--- a/juneau-utest/src/test/java/org/apache/juneau/junit/PropertyExtractors.java
+++ b/juneau-utest/src/test/java/org/apache/juneau/junit/PropertyExtractors.java
@@ -247,6 +247,7 @@ public class PropertyExtractors {
                @Override
                public Object extract(BeanConverter converter, Object o, String 
name) {
                        var m = (Map<?,?>)o;
+                       if (eq(name, 
converter.getSetting(BasicBeanConverter.SETTING_nullValue, "<null>"))) name = 
null;
                        if (m.containsKey(name)) return m.get(name);
                        if ("size".equals(name)) return m.size();
                        return super.extract(converter, o, name);
diff --git 
a/juneau-utest/src/test/java/org/apache/juneau/junit/PropertyExtractors_Test.java
 
b/juneau-utest/src/test/java/org/apache/juneau/junit/PropertyExtractors_Test.java
index e0811338d..637cbe68a 100644
--- 
a/juneau-utest/src/test/java/org/apache/juneau/junit/PropertyExtractors_Test.java
+++ 
b/juneau-utest/src/test/java/org/apache/juneau/junit/PropertyExtractors_Test.java
@@ -39,7 +39,7 @@ class PropertyExtractors_Test extends TestBase {
 
        @Nested
        @DisplayName("ObjectPropertyExtractor")
-       class ObjectPropertyExtractorTest {
+       class a_ObjectPropertyExtractorTest {
 
                private PropertyExtractors.ObjectPropertyExtractor extractor = 
new PropertyExtractors.ObjectPropertyExtractor();
 
@@ -129,7 +129,7 @@ class PropertyExtractors_Test extends TestBase {
 
        @Nested
        @DisplayName("ListPropertyExtractor")
-       class ListPropertyExtractorTest {
+       class b_ListPropertyExtractorTest {
 
                private PropertyExtractors.ListPropertyExtractor extractor = 
new PropertyExtractors.ListPropertyExtractor();
 
@@ -208,7 +208,7 @@ class PropertyExtractors_Test extends TestBase {
 
        @Nested
        @DisplayName("MapPropertyExtractor")
-       class MapPropertyExtractorTest {
+       class c_MapPropertyExtractorTest {
 
                private PropertyExtractors.MapPropertyExtractor extractor = new 
PropertyExtractors.MapPropertyExtractor();
 
@@ -316,9 +316,9 @@ class PropertyExtractors_Test extends TestBase {
                public int getAge() { return age; }
                public boolean isActive() { return active; }
 
-               public void setName(String name) { this.name = name; }
-               public void setAge(int age) { this.age = age; }
-               public void setActive(boolean active) { this.active = active; }
+               void setName(String name) { this.name = name; }
+               void setAge(int age) { this.age = age; }
+               void setActive(boolean active) { this.active = active; }
        }
 
        public static class TestBeanWithFields {
diff --git 
a/juneau-utest/src/test/java/org/apache/juneau/junit/Stringifiers.java 
b/juneau-utest/src/test/java/org/apache/juneau/junit/Stringifiers.java
new file mode 100644
index 000000000..46b8714f8
--- /dev/null
+++ b/juneau-utest/src/test/java/org/apache/juneau/junit/Stringifiers.java
@@ -0,0 +1,650 @@
+// 
***************************************************************************************************************************
+// * 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.juneau.junit;
+
+import static java.time.format.DateTimeFormatter.*;
+import static java.util.stream.Collectors.*;
+import static org.apache.juneau.junit.Utils.*;
+
+import java.io.*;
+import java.lang.reflect.*;
+import java.nio.file.*;
+import java.util.*;
+
+/**
+ * Collection of standard stringifier implementations for the Bean-Centric 
Testing framework.
+ *
+ * <p>This class provides built-in string conversion strategies that handle 
common Java types
+ * and objects. These stringifiers are automatically registered when using
+ * {@link BasicBeanConverter.Builder#defaultSettings()}.</p>
+ *
+ * <h5 class='section'>Purpose:</h5>
+ * <p>Stringifiers convert objects to human-readable string representations 
for use in BCT
+ * assertions and test output. They provide consistent, meaningful string 
formats across
+ * different object types while supporting customization for specific testing 
needs.</p>
+ *
+ * <h5 class='section'>Built-in Stringifiers:</h5>
+ * <ul>
+ *     <li><b>{@link #mapEntryStringifier()}</b> - Converts {@link Map.Entry} 
to <js>"key=value"</js> format</li>
+ *     <li><b>{@link #calendarStringifier()}</b> - Converts {@link 
GregorianCalendar} to ISO-8601 format</li>
+ *     <li><b>{@link #dateStringifier()}</b> - Converts {@link Date} to ISO 
instant format</li>
+ *     <li><b>{@link #inputStreamStringifier()}</b> - Converts {@link 
InputStream} content to hex strings</li>
+ *     <li><b>{@link #byteArrayStringifier()}</b> - Converts byte arrays to 
hex strings</li>
+ *     <li><b>{@link #readerStringifier()}</b> - Converts {@link Reader} 
content to strings</li>
+ *     <li><b>{@link #fileStringifier()}</b> - Converts {@link File} content 
to strings</li>
+ *     <li><b>{@link #enumStringifier()}</b> - Converts {@link Enum} values to 
name format</li>
+ *     <li><b>{@link #classStringifier()}</b> - Converts {@link Class} objects 
to name format</li>
+ *     <li><b>{@link #constructorStringifier()}</b> - Converts {@link 
Constructor} to signature format</li>
+ *     <li><b>{@link #methodStringifier()}</b> - Converts {@link Method} to 
signature format</li>
+ *     <li><b>{@link #listStringifier()}</b> - Converts {@link List} to 
bracket-delimited format</li>
+ *     <li><b>{@link #mapStringifier()}</b> - Converts {@link Map} to 
brace-delimited format</li>
+ * </ul>
+ *
+ * <h5 class='section'>Usage Example:</h5>
+ * <p class='bjava'>
+ *     <jc>// Register stringifiers using builder</jc>
+ *     <jk>var</jk> <jv>converter</jv> = 
BasicBeanConverter.<jsm>builder</jsm>()
+ *             .defaultSettings()
+ *             .addStringifier(Date.<jk>class</jk>, 
Stringifiers.<jsm>dateStringifier</jsm>())
+ *             .addStringifier(File.<jk>class</jk>, 
Stringifiers.<jsm>fileStringifier</jsm>())
+ *             .build();
+ * </p>
+ *
+ * <h5 class='section'>Resource Handling:</h5>
+ * <p><b>Warning:</b> Some stringifiers consume or close their input 
resources:</p>
+ * <ul>
+ *     <li><b>{@link InputStream}:</b> Stream is consumed and closed during 
stringification</li>
+ *     <li><b>{@link Reader}:</b> Reader is consumed and closed during 
stringification</li>
+ *     <li><b>{@link File}:</b> File content is read completely during 
stringification</li>
+ * </ul>
+ *
+ * <h5 class='section'>Custom Stringifier Development:</h5>
+ * <p>When creating custom stringifiers, follow these patterns:</p>
+ * <ul>
+ *     <li><b>Null Safety:</b> Handle <jk>null</jk> inputs gracefully</li>
+ *     <li><b>Resource Management:</b> Properly close resources after use</li>
+ *     <li><b>Exception Handling:</b> Convert exceptions to meaningful error 
messages</li>
+ *     <li><b>Performance:</b> Consider string building efficiency for complex 
objects</li>
+ *     <li><b>Readability:</b> Ensure output is useful for debugging and 
assertions</li>
+ * </ul>
+ *
+ * @see Stringifier
+ * @see BasicBeanConverter.Builder#addStringifier(Class, Stringifier)
+ * @see BasicBeanConverter.Builder#defaultSettings()
+ */
+@SuppressWarnings({"rawtypes"})
+public class Stringifiers {
+
+       private static final char[] HEX = "0123456789ABCDEF".toCharArray();
+
+       /**
+        * Constructor.
+        */
+       private Stringifiers() {}
+
+       /**
+        * Returns a stringifier for {@link Map.Entry} objects that formats 
them as <js>"key=value"</js>.
+        *
+        * <p>This stringifier creates a human-readable representation of map 
entries by converting
+        * both the key and value to strings and joining them with the 
configured entry separator.</p>
+        *
+        * <h5 class='section'>Behavior:</h5>
+        * <ul>
+        *      <li><b>Format:</b> Uses the pattern 
<js>"{key}{separator}{value}"</js></li>
+        *      <li><b>Separator:</b> Uses the {@code mapEntrySeparator} 
setting (default: <js>"="</js>)</li>
+        *      <li><b>Recursive conversion:</b> Both key and value are 
converted using the same converter</li>
+        * </ul>
+        *
+        * <h5 class='section'>Usage Examples:</h5>
+        * <p class='bjava'>
+        *      <jc>// Test map entry stringification</jc>
+        *      <jk>var</jk> <jv>entry</jv> = 
Map.<jsm>entry</jsm>(<js>"name"</js>, <js>"John"</js>);
+        *      <jsm>assertBean</jsm>(<jv>entry</jv>, <js>"&lt;self&gt;"</js>, 
<js>"name=John"</js>);
+        *
+        *      <jc>// Test with custom separator</jc>
+        *      <jk>var</jk> <jv>converter</jv> = 
BasicBeanConverter.<jsm>builder</jsm>()
+        *              .defaultSettings()
+        *              .addSetting(<jsf>SETTING_mapEntrySeparator</jsf>, 
<js>": "</js>)
+        *              .build();
+        *      <jsm>assertBean</jsm>(<jv>entry</jv>, <js>"&lt;self&gt;"</js>, 
<js>"name: John"</js>);
+        * </p>
+        *
+        * @return A {@link Stringifier} for {@link Map.Entry} objects
+        * @see Map.Entry
+        */
+       public static Stringifier<Map.Entry> mapEntryStringifier() {
+               return (bc, entry) -> bc.stringify(entry.getKey()) + 
bc.getSetting("mapEntrySeparator", "=") + bc.stringify(entry.getValue());
+       }
+
+       /**
+        * Returns a stringifier for {@link GregorianCalendar} objects that 
formats them as ISO-8601 strings.
+        *
+        * <p>This stringifier converts calendar objects to standardized 
ISO-8601 timestamp format,
+        * which provides consistent, sortable, and internationally recognized 
date representations.</p>
+        *
+        * <h5 class='section'>Behavior:</h5>
+        * <ul>
+        *      <li><b>Format:</b> Uses the {@code calendarFormat} setting 
(default: {@link java.time.format.DateTimeFormatter#ISO_INSTANT})</li>
+        *      <li><b>Timezone:</b> Respects the calendar's timezone 
information</li>
+        *      <li><b>Precision:</b> Includes full precision available in the 
calendar</li>
+        * </ul>
+        *
+        * <h5 class='section'>Usage Examples:</h5>
+        * <p class='bjava'>
+        *      <jc>// Test calendar stringification</jc>
+        *      <jk>var</jk> <jv>calendar</jv> = <jk>new</jk> 
GregorianCalendar(<jv>2023</jv>, Calendar.<jsf>JANUARY</jsf>, <jv>15</jv>);
+        *      <jsm>assertMatchesGlob</jsm>(<js>"2023-01-*"</js>, 
<jv>calendar</jv>);
+        *
+        *      <jc>// Test with custom format</jc>
+        *      <jk>var</jk> <jv>converter</jv> = 
BasicBeanConverter.<jsm>builder</jsm>()
+        *              .defaultSettings()
+        *              .addSetting(<jsf>SETTING_calendarFormat</jsf>, 
DateTimeFormatter.<jsf>ISO_LOCAL_DATE</jsf>)
+        *              .build();
+        * </p>
+        *
+        * @return A {@link Stringifier} for {@link GregorianCalendar} objects
+        * @see GregorianCalendar
+        * @see java.time.format.DateTimeFormatter#ISO_INSTANT
+        */
+       public static Stringifier<GregorianCalendar> calendarStringifier() {
+               return (bc, calendar) -> 
calendar.toZonedDateTime().format(bc.getSetting("calendarFormat", ISO_INSTANT));
+       }
+
+       /**
+        * Returns a stringifier for {@link Date} objects that formats them as 
ISO instant strings.
+        *
+        * <p>This stringifier converts Date objects to ISO-8601 instant 
format, providing
+        * standardized timestamp representations suitable for logging and 
comparison.</p>
+        *
+        * <h5 class='section'>Behavior:</h5>
+        * <ul>
+        *      <li><b>Format:</b> ISO-8601 instant format (e.g., 
<js>"2023-01-15T10:30:00Z"</js>)</li>
+        *      <li><b>Timezone:</b> Always represents time in UTC (Z 
timezone)</li>
+        *      <li><b>Precision:</b> Millisecond precision as available in 
Date objects</li>
+        * </ul>
+        *
+        * <h5 class='section'>Usage Examples:</h5>
+        * <p class='bjava'>
+        *      <jc>// Test date stringification</jc>
+        *      <jk>var</jk> <jv>date</jv> = <jk>new</jk> 
Date(<jv>1673780400000L</jv>); <jc>// 2023-01-15T10:00:00Z</jc>
+        *      <jsm>assertBean</jsm>(<jv>date</jv>, <js>"&lt;self&gt;"</js>, 
<js>"2023-01-15T10:00:00Z"</js>);
+        *
+        *      <jc>// Test in object property</jc>
+        *      <jk>var</jk> <jv>event</jv> = <jk>new</jk> 
Event().setTimestamp(<jv>date</jv>);
+        *      <jsm>assertBean</jsm>(<jv>event</jv>, <js>"timestamp"</js>, 
<js>"2023-01-15T10:00:00Z"</js>);
+        * </p>
+        *
+        * @return A {@link Stringifier} for {@link Date} objects
+        * @see Date
+        */
+       public static Stringifier<Date> dateStringifier() {
+               return (bc, date) -> date.toInstant().toString();
+       }
+
+       /**
+        * Returns a stringifier for {@link InputStream} objects that converts 
content to hex strings.
+        *
+        * <p><b>Warning:</b> This stringifier consumes and closes the input 
stream during conversion.
+        * After stringification, the stream cannot be used again.</p>
+        *
+        * <h5 class='section'>Behavior:</h5>
+        * <ul>
+        *      <li><b>Content reading:</b> Reads all available bytes from the 
stream</li>
+        *      <li><b>Hex conversion:</b> Converts bytes to uppercase 
hexadecimal representation</li>
+        *      <li><b>Resource management:</b> Automatically closes the stream 
after reading</li>
+        * </ul>
+        *
+        * <h5 class='section'>Usage Examples:</h5>
+        * <p class='bjava'>
+        *      <jc>// Test with byte content</jc>
+        *      <jk>var</jk> <jv>stream</jv> = <jk>new</jk> 
ByteArrayInputStream(<jk>new</jk> <jk>byte</jk>[]{<jv>0x48</jv>, <jv>0x65</jv>, 
<jv>0x6C</jv>, <jv>0x6C</jv>, <jv>0x6F</jv>});
+        *      <jsm>assertBean</jsm>(<jv>stream</jv>, <js>"&lt;self&gt;"</js>, 
<js>"48656C6C6F"</js>); <jc>// "Hello" in hex</jc>
+        *
+        *      <jc>// Test empty stream</jc>
+        *      <jk>var</jk> <jv>empty</jv> = <jk>new</jk> 
ByteArrayInputStream(<jk>new</jk> <jk>byte</jk>[<jv>0</jv>]);
+        *      <jsm>assertBean</jsm>(<jv>empty</jv>, <js>"&lt;self&gt;"</js>, 
<js>""</js>);
+        * </p>
+        *
+        * <h5 class='section'>Important Notes:</h5>
+        * <ul>
+        *      <li><b>One-time use:</b> The stream is consumed and closed 
during conversion</li>
+        *      <li><b>Memory usage:</b> All content is loaded into memory for 
conversion</li>
+        *      <li><b>Exception handling:</b> IO exceptions are wrapped in 
RuntimeException</li>
+        * </ul>
+        *
+        * @return A {@link Stringifier} for {@link InputStream} objects
+        * @see InputStream
+        */
+       public static Stringifier<InputStream> inputStreamStringifier() {
+               return (bc, stream) -> stringifyInputStream(stream);
+       }
+
+       /**
+        * Returns a stringifier for byte arrays that converts them to hex 
strings.
+        *
+        * <p>This stringifier provides a consistent way to represent binary 
data as readable
+        * hexadecimal strings, useful for testing and debugging binary 
content.</p>
+        *
+        * <h5 class='section'>Behavior:</h5>
+        * <ul>
+        *      <li><b>Hex format:</b> Each byte is represented as two 
uppercase hex digits</li>
+        *      <li><b>No separators:</b> Bytes are concatenated without spaces 
or delimiters</li>
+        *      <li><b>Empty arrays:</b> Returns empty string for zero-length 
arrays</li>
+        * </ul>
+        *
+        * <h5 class='section'>Usage Examples:</h5>
+        * <p class='bjava'>
+        *      <jc>// Test byte array stringification</jc>
+        *      <jk>byte</jk>[] <jv>data</jv> = {<jv>0x48</jv>, <jv>0x65</jv>, 
<jv>0x6C</jv>, <jv>0x6C</jv>, <jv>0x6F</jv>};
+        *      <jsm>assertBean</jsm>(<jv>data</jv>, <js>"&lt;self&gt;"</js>, 
<js>"48656C6C6F"</js>); <jc>// "Hello" in hex</jc>
+        *
+        *      <jc>// Test with zeros and high values</jc>
+        *      <jk>byte</jk>[] <jv>mixed</jv> = {<jv>0x00</jv>, <jv>0xFF</jv>, 
<jv>0x7F</jv>};
+        *      <jsm>assertBean</jsm>(<jv>mixed</jv>, <js>"&lt;self&gt;"</js>, 
<js>"00FF7F"</js>);
+        * </p>
+        *
+        * @return A {@link Stringifier} for byte arrays
+        */
+       public static Stringifier<byte[]> byteArrayStringifier() {
+               return (bc, bytes) -> {
+                       var sb = new StringBuilder(bytes.length * 2);
+                       for (var element : bytes) {
+                               var v = element & 0xFF;
+                               sb.append(HEX[v >>> 4]).append(HEX[v & 0x0F]);
+                       }
+                       return sb.toString();
+               };
+       }
+
+       /**
+        * Returns a stringifier for {@link Reader} objects that converts 
content to strings.
+        *
+        * <p><b>Warning:</b> This stringifier consumes and closes the reader 
during conversion.
+        * After stringification, the reader cannot be used again.</p>
+        *
+        * <h5 class='section'>Behavior:</h5>
+        * <ul>
+        *      <li><b>Content reading:</b> Reads all available characters from 
the reader</li>
+        *      <li><b>String conversion:</b> Converts characters directly to 
string format</li>
+        *      <li><b>Resource management:</b> Automatically closes the reader 
after reading</li>
+        * </ul>
+        *
+        * <h5 class='section'>Usage Examples:</h5>
+        * <p class='bjava'>
+        *      <jc>// Test with string content</jc>
+        *      <jk>var</jk> <jv>reader</jv> = <jk>new</jk> 
StringReader(<js>"Hello World"</js>);
+        *      <jsm>assertBean</jsm>(<jv>reader</jv>, <js>"&lt;self&gt;"</js>, 
<js>"Hello World"</js>);
+        *
+        *      <jc>// Test with file reader</jc>
+        *      <jk>var</jk> <jv>fileReader</jv> = 
Files.<jsm>newBufferedReader</jsm>(path);
+        *      <jsm>assertMatchesGlob</jsm>(<js>"*expected content*"</js>, 
<jv>fileReader</jv>);
+        * </p>
+        *
+        * <h5 class='section'>Important Notes:</h5>
+        * <ul>
+        *      <li><b>One-time use:</b> The reader is consumed and closed 
during conversion</li>
+        *      <li><b>Memory usage:</b> All content is loaded into memory for 
conversion</li>
+        *      <li><b>Exception handling:</b> IO exceptions are wrapped in 
RuntimeException</li>
+        * </ul>
+        *
+        * @return A {@link Stringifier} for {@link Reader} objects
+        * @see Reader
+        */
+       public static Stringifier<Reader> readerStringifier() {
+               return (bc, reader) -> stringifyReader(reader);
+       }
+
+       /**
+        * Returns a stringifier for {@link File} objects that converts file 
content to strings.
+        *
+        * <p>This stringifier reads the entire file content and returns it as 
a string,
+        * making it useful for testing file-based operations and content 
verification.</p>
+        *
+        * <h5 class='section'>Behavior:</h5>
+        * <ul>
+        *      <li><b>Content reading:</b> Reads the entire file content into 
memory</li>
+        *      <li><b>Encoding:</b> Uses the default platform encoding for 
text files</li>
+        *      <li><b>Resource management:</b> Properly closes file resources 
after reading</li>
+        * </ul>
+        *
+        * <h5 class='section'>Usage Examples:</h5>
+        * <p class='bjava'>
+        *      <jc>// Test file content</jc>
+        *      <jk>var</jk> <jv>configFile</jv> = <jk>new</jk> 
File(<js>"config.properties"</js>);
+        *      <jsm>assertMatchesGlob</jsm>(<js>"*database.url=*"</js>, 
<jv>configFile</jv>);
+        *
+        *      <jc>// Test empty file</jc>
+        *      <jk>var</jk> <jv>emptyFile</jv> = <jk>new</jk> 
File(<js>"empty.txt"</js>);
+        *      <jsm>assertBean</jsm>(<jv>emptyFile</jv>, 
<js>"&lt;self&gt;"</js>, <js>""</js>);
+        * </p>
+        *
+        * <h5 class='section'>Important Notes:</h5>
+        * <ul>
+        *      <li><b>Memory usage:</b> Large files will consume significant 
memory</li>
+        *      <li><b>File existence:</b> Non-existent files will cause 
exceptions</li>
+        *      <li><b>Binary files:</b> May produce unexpected results with 
binary content</li>
+        *      <li><b>Exception handling:</b> IO exceptions are wrapped in 
RuntimeException</li>
+        * </ul>
+        *
+        * @return A {@link Stringifier} for {@link File} objects
+        * @see File
+        */
+       public static Stringifier<File> fileStringifier() {
+               return (bc, file) -> safe(() -> 
stringifyReader(Files.newBufferedReader(file.toPath())));
+       }
+
+       /**
+        * Returns a stringifier for {@link Enum} objects that converts them to 
name format.
+        *
+        * <p>This stringifier provides a consistent way to represent enum 
values as their
+        * declared constant names, which is typically the most useful format 
for testing.</p>
+        *
+        * <h5 class='section'>Behavior:</h5>
+        * <ul>
+        *      <li><b>Name format:</b> Uses {@link Enum#name()} method for 
string representation</li>
+        *      <li><b>Case preservation:</b> Maintains the exact case as 
declared in enum</li>
+        *      <li><b>All enum types:</b> Works with any enum 
implementation</li>
+        * </ul>
+        *
+        * <h5 class='section'>Usage Examples:</h5>
+        * <p class='bjava'>
+        *      <jc>// Test enum stringification</jc>
+        *      <jsm>assertBean</jsm>(Color.<jsf>RED</jsf>, 
<js>"&lt;self&gt;"</js>, <js>"RED"</js>);
+        *      <jsm>assertBean</jsm>(Status.<jsf>IN_PROGRESS</jsf>, 
<js>"&lt;self&gt;"</js>, <js>"IN_PROGRESS"</js>);
+        *
+        *      <jc>// Test in object property</jc>
+        *      <jk>var</jk> <jv>task</jv> = <jk>new</jk> 
Task().setStatus(Status.<jsf>COMPLETED</jsf>);
+        *      <jsm>assertBean</jsm>(<jv>task</jv>, <js>"status"</js>, 
<js>"COMPLETED"</js>);
+        * </p>
+        *
+        * <h5 class='section'>Alternative Formats:</h5>
+        * <p>If you need different enum string representations (like {@link 
Enum#toString()}
+        * or custom formatting), register a custom stringifier for specific 
enum types.</p>
+        *
+        * @return A {@link Stringifier} for {@link Enum} objects
+        * @see Enum
+        * @see Enum#name()
+        */
+       public static Stringifier<Enum> enumStringifier() {
+               return (bc, enumValue) -> enumValue.name();
+       }
+
+       /**
+        * Returns a stringifier for {@link Class} objects that formats them 
according to configured settings.
+        *
+        * <p>This stringifier provides flexible class name formatting, 
supporting different
+        * levels of detail from simple names to fully qualified class 
names.</p>
+        *
+        * <h5 class='section'>Behavior:</h5>
+        * <ul>
+        *      <li><b>Format options:</b> Controlled by {@code 
classNameFormat} setting</li>
+        *      <li><b>Simple format:</b> Class simple name (default)</li>
+        *      <li><b>Canonical format:</b> Fully qualified canonical name</li>
+        *      <li><b>Full format:</b> Complete class name including 
package</li>
+        * </ul>
+        *
+        * <h5 class='section'>Usage Examples:</h5>
+        * <p class='bjava'>
+        *      <jc>// Test with default simple format</jc>
+        *      <jsm>assertBean</jsm>(String.<jk>class</jk>, 
<js>"&lt;self&gt;"</js>, <js>"String"</js>);
+        *      <jsm>assertBean</jsm>(ArrayList.<jk>class</jk>, 
<js>"&lt;self&gt;"</js>, <js>"ArrayList"</js>);
+        *
+        *      <jc>// Test with canonical format</jc>
+        *      <jk>var</jk> <jv>converter</jv> = 
BasicBeanConverter.<jsm>builder</jsm>()
+        *              .defaultSettings()
+        *              .addSetting(<jsf>SETTING_classNameFormat</jsf>, 
<js>"canonical"</js>)
+        *              .build();
+        *      <jsm>assertBean</jsm>(String.<jk>class</jk>, 
<js>"&lt;self&gt;"</js>, <js>"java.lang.String"</js>);
+        * </p>
+        *
+        * @return A {@link Stringifier} for {@link Class} objects
+        * @see Class
+        */
+       public static Stringifier<Class> classStringifier() {
+               return (bc, clazz) -> stringifyClass(bc, clazz);
+       }
+
+       /**
+        * Returns a stringifier for {@link Constructor} objects that formats 
them as readable signatures.
+        *
+        * <p>This stringifier creates human-readable constructor signatures 
including the
+        * declaring class name and parameter types, useful for 
reflection-based testing.</p>
+        *
+        * <h5 class='section'>Behavior:</h5>
+        * <ul>
+        *      <li><b>Format:</b> 
<js>"{ClassName}({paramType1},{paramType2},...)"</js></li>
+        *      <li><b>Class names:</b> Uses the configured class name 
format</li>
+        *      <li><b>Parameter types:</b> Includes all parameter types in 
declaration order</li>
+        * </ul>
+        *
+        * <h5 class='section'>Usage Examples:</h5>
+        * <p class='bjava'>
+        *      <jc>// Test constructor stringification</jc>
+        *      <jk>var</jk> <jv>constructor</jv> = 
String.<jk>class</jk>.getConstructor(<jk>char</jk>[].<jk>class</jk>);
+        *      <jsm>assertBean</jsm>(<jv>constructor</jv>, 
<js>"&lt;self&gt;"</js>, <js>"String(char[])"</js>);
+        *
+        *      <jc>// Test no-arg constructor</jc>
+        *      <jk>var</jk> <jv>defaultConstructor</jv> = 
ArrayList.<jk>class</jk>.getConstructor();
+        *      <jsm>assertBean</jsm>(<jv>defaultConstructor</jv>, 
<js>"&lt;self&gt;"</js>, <js>"ArrayList()"</js>);
+        * </p>
+        *
+        * @return A {@link Stringifier} for {@link Constructor} objects
+        * @see Constructor
+        */
+       public static Stringifier<Constructor> constructorStringifier() {
+               return (bc, constructor) -> {
+                       return new StringBuilder()
+                               .append(stringifyClass(bc, ((Constructor<?>) 
constructor).getDeclaringClass()))
+                               .append('(')
+                               
.append(Arrays.stream((constructor).getParameterTypes())
+                               .map(x -> stringifyClass(bc, x))
+                               .collect(joining(",")))
+                               .append(')')
+                               .toString();
+               };
+       }
+
+       /**
+        * Returns a stringifier for {@link Method} objects that formats them 
as readable signatures.
+        *
+        * <p>This stringifier creates human-readable method signatures 
including the method
+        * name and parameter types, useful for reflection-based testing and 
debugging.</p>
+        *
+        * <h5 class='section'>Behavior:</h5>
+        * <ul>
+        *      <li><b>Format:</b> 
<js>"{methodName}({paramType1},{paramType2},...)"</js></li>
+        *      <li><b>Method name:</b> Uses the declared method name</li>
+        *      <li><b>Parameter types:</b> Includes all parameter types in 
declaration order</li>
+        * </ul>
+        *
+        * <h5 class='section'>Usage Examples:</h5>
+        * <p class='bjava'>
+        *      <jc>// Test method stringification</jc>
+        *      <jk>var</jk> <jv>method</jv> = 
String.<jk>class</jk>.getMethod(<js>"substring"</js>, 
<jk>int</jk>.<jk>class</jk>, <jk>int</jk>.<jk>class</jk>);
+        *      <jsm>assertBean</jsm>(<jv>method</jv>, <js>"&lt;self&gt;"</js>, 
<js>"substring(int,int)"</js>);
+        *
+        *      <jc>// Test no-arg method</jc>
+        *      <jk>var</jk> <jv>toString</jv> = 
Object.<jk>class</jk>.getMethod(<js>"toString"</js>);
+        *      <jsm>assertBean</jsm>(<jv>toString</jv>, 
<js>"&lt;self&gt;"</js>, <js>"toString()"</js>);
+        * </p>
+        *
+        * @return A {@link Stringifier} for {@link Method} objects
+        * @see Method
+        */
+       public static Stringifier<Method> methodStringifier() {
+               return (bc, method) -> {
+                       return new StringBuilder()
+                               .append(method.getName())
+                               .append('(')
+                               
.append(Arrays.stream(method.getParameterTypes())
+                               .map(x -> stringifyClass(bc, x))
+                               .collect(joining(",")))
+                               .append(')')
+                               .toString();
+               };
+       }
+
+       /**
+        * Returns a stringifier for {@link List} objects that formats them 
with configurable delimiters.
+        *
+        * <p>This stringifier converts lists to bracket-delimited strings with 
customizable
+        * separators and prefixes/suffixes, providing consistent list 
representation across tests.</p>
+        *
+        * <h5 class='section'>Behavior:</h5>
+        * <ul>
+        *      <li><b>Format:</b> 
<js>"{prefix}{element1}{separator}{element2}...{suffix}"</js></li>
+        *      <li><b>Separator:</b> Uses {@code fieldSeparator} setting 
(default: <js>","</js>)</li>
+        *      <li><b>Prefix:</b> Uses {@code collectionPrefix} setting 
(default: <js>"["</js>)</li>
+        *      <li><b>Suffix:</b> Uses {@code collectionSuffix} setting 
(default: <js>"]"</js>)</li>
+        *      <li><b>Recursive:</b> Elements are converted using the same 
converter</li>
+        * </ul>
+        *
+        * <h5 class='section'>Usage Examples:</h5>
+        * <p class='bjava'>
+        *      <jc>// Test list stringification</jc>
+        *      <jk>var</jk> <jv>list</jv> = 
List.<jsm>of</jsm>(<js>"apple"</js>, <js>"banana"</js>, <js>"cherry"</js>);
+        *      <jsm>assertBean</jsm>(<jv>list</jv>, <js>"&lt;self&gt;"</js>, 
<js>"[apple,banana,cherry]"</js>);
+        *
+        *      <jc>// Test with custom formatting</jc>
+        *      <jk>var</jk> <jv>converter</jv> = 
BasicBeanConverter.<jsm>builder</jsm>()
+        *              .defaultSettings()
+        *              .addSetting(<jsf>SETTING_fieldSeparator</jsf>, <js>"; 
"</js>)
+        *              .addSetting(<jsf>SETTING_collectionPrefix</jsf>, 
<js>"("</js>)
+        *              .addSetting(<jsf>SETTING_collectionSuffix</jsf>, 
<js>")"</js>)
+        *              .build();
+        *      <jsm>assertBean</jsm>(<jv>list</jv>, <js>"&lt;self&gt;"</js>, 
<js>"(apple; banana; cherry)"</js>);
+        * </p>
+        *
+        * @return A {@link Stringifier} for {@link List} objects
+        * @see List
+        */
+       public static Stringifier<List> listStringifier() {
+               return (bc, list) -> ((List<?>)list).stream()
+                       .map(bc::stringify)
+                       .collect(joining(
+                               bc.getSetting("fieldSeparator", ","),
+                               bc.getSetting("collectionPrefix", "["),
+                               bc.getSetting("collectionSuffix", "]")
+                       ));
+       }
+
+       /**
+        * Returns a stringifier for {@link Map} objects that formats them with 
configurable delimiters.
+        *
+        * <p>This stringifier converts maps to brace-delimited strings by 
first converting the
+        * map to a list of entries and then stringifying each entry, providing 
consistent
+        * map representation across tests.</p>
+        *
+        * <h5 class='section'>Behavior:</h5>
+        * <ul>
+        *      <li><b>Format:</b> 
<js>"{prefix}{entry1}{separator}{entry2}...{suffix}"</js></li>
+        *      <li><b>Separator:</b> Uses {@code fieldSeparator} setting 
(default: <js>","</js>)</li>
+        *      <li><b>Prefix:</b> Uses {@code mapPrefix} setting (default: 
<js>"{"</js>)</li>
+        *      <li><b>Suffix:</b> Uses {@code mapSuffix} setting (default: 
<js>"}"</js>)</li>
+        *      <li><b>Entry format:</b> Each entry uses the map entry 
stringifier</li>
+        * </ul>
+        *
+        * <h5 class='section'>Usage Examples:</h5>
+        * <p class='bjava'>
+        *      <jc>// Test map stringification</jc>
+        *      <jk>var</jk> <jv>map</jv> = Map.<jsm>of</jsm>(<js>"name"</js>, 
<js>"John"</js>, <js>"age"</js>, <jv>25</jv>);
+        *      <jsm>assertMatchesGlob</jsm>(<js>"{*name=John*age=25*}"</js>, 
<jv>map</jv>);
+        *
+        *      <jc>// Test empty map</jc>
+        *      <jk>var</jk> <jv>emptyMap</jv> = Map.<jsm>of</jsm>();
+        *      <jsm>assertBean</jsm>(<jv>emptyMap</jv>, 
<js>"&lt;self&gt;"</js>, <js>"{}"</js>);
+        * </p>
+        *
+        * <h5 class='section'>Order Considerations:</h5>
+        * <p>The order of entries in the string depends on the map 
implementation's iteration
+        * order. Use order-independent assertions (like {@code 
assertMatchesGlob}) for maps where
+        * order is not guaranteed.</p>
+        *
+        * @return A {@link Stringifier} for {@link Map} objects
+        * @see Map
+        * @see Map.Entry
+        */
+       public static Stringifier<Map> mapStringifier() {
+               return (bc, map) -> ((Map<?,?>)map).entrySet().stream()
+                       .map(bc::stringify)
+                       .collect(joining(
+                               bc.getSetting("fieldSeparator", ","),
+                               bc.getSetting("mapPrefix", "{"),
+                               bc.getSetting("mapSuffix", "}")
+                       ));
+       }
+
+       
//---------------------------------------------------------------------------------------------
+       // Helper methods for internal stringification
+       
//---------------------------------------------------------------------------------------------
+
+       /**
+        * Converts an InputStream to a hexadecimal string representation.
+        *
+        * @param stream The InputStream to convert
+        * @return Hexadecimal string representation of the stream content
+        */
+       private static String stringifyInputStream(InputStream stream) {
+               return safe(() -> {
+                       try (var o2 = stream) {
+                               var buff = new ByteArrayOutputStream(1024);
+                               var nRead = 0;
+                               var b = new byte[1024];
+                               while ((nRead = o2.read(b, 0, b.length)) != -1)
+                                       buff.write(b, 0, nRead);
+                               buff.flush();
+                               byte[] bytes = buff.toByteArray();
+                               var sb = new StringBuilder(bytes.length * 2);
+                               for (var element : bytes) {
+                                       var v = element & 0xFF;
+                                       sb.append(HEX[v >>> 4]).append(HEX[v & 
0x0F]);
+                               }
+                               return sb.toString();
+                       }
+               });
+       }
+
+       /**
+        * Converts a Reader to a string representation.
+        *
+        * @param reader The Reader to convert
+        * @return String content from the reader
+        */
+       private static String stringifyReader(Reader reader) {
+               return safe(() -> {
+                       try (var o2 = reader) {
+                               var sb = new StringBuilder();
+                               var buf = new char[1024];
+                               var i = 0;
+                               while ((i = o2.read(buf)) != -1)
+                                       sb.append(buf, 0, i);
+                               return sb.toString();
+                       }
+               });
+       }
+
+       /**
+        * Converts a Class to a string representation based on converter 
settings.
+        *
+        * @param bc The bean converter for accessing settings
+        * @param clazz The Class to convert
+        * @return String representation of the class
+        */
+       private static String stringifyClass(BeanConverter bc, Class<?> clazz) {
+               return switch(bc.getSetting("classNameFormat", "default")) {
+                       case "simple" -> clazz.getSimpleName();
+                       case "canonical" -> clazz.getCanonicalName();
+                       default -> clazz.getName();
+               };
+       }
+}
diff --git 
a/juneau-utest/src/test/java/org/apache/juneau/junit/Stringifiers_Test.java 
b/juneau-utest/src/test/java/org/apache/juneau/junit/Stringifiers_Test.java
new file mode 100644
index 000000000..0cac7a924
--- /dev/null
+++ b/juneau-utest/src/test/java/org/apache/juneau/junit/Stringifiers_Test.java
@@ -0,0 +1,529 @@
+// 
***************************************************************************************************************************
+// * 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.juneau.junit;
+
+import static org.apache.juneau.junit.Assertions2.*;
+import static org.junit.jupiter.api.Assertions.*;
+
+import java.io.*;
+import java.nio.file.*;
+import java.time.format.*;
+import java.util.*;
+
+import org.junit.jupiter.api.*;
+import org.junit.jupiter.params.*;
+import org.junit.jupiter.params.provider.*;
+
+/**
+ * Unit tests for {@link Stringifiers}.
+ */
+class Stringifiers_Test extends TestBase {
+
+       @Nested
+       class a_MapEntryStringifier {
+
+               @Test
+               void a01_stringifySimpleEntry() {
+                       var converter = 
BasicBeanConverter.builder().defaultSettings().build();
+                       var stringifier = Stringifiers.mapEntryStringifier();
+                       var entry = Map.entry("key", "value");
+                       var result = stringifier.apply(converter, entry);
+
+                       assertEquals("key=value", result);
+               }
+
+               @Test
+               void a02_stringifyWithNullKey() {
+                       var converter = 
BasicBeanConverter.builder().defaultSettings().build();
+                       var stringifier = Stringifiers.mapEntryStringifier();
+                       var entry = new AbstractMap.SimpleEntry<String, 
String>(null, "value");
+                       var result = stringifier.apply(converter, entry);
+
+                       assertEquals("<null>=value", result);
+               }
+
+               @Test
+               void a03_stringifyWithNullValue() {
+                       var converter = 
BasicBeanConverter.builder().defaultSettings().build();
+                       var stringifier = Stringifiers.mapEntryStringifier();
+                       var entry = new AbstractMap.SimpleEntry<>("key", null);
+                       var result = stringifier.apply(converter, entry);
+
+                       assertEquals("key=<null>", result);
+               }
+
+               @Test
+               void a04_stringifyWithCustomSeparator() {
+                       var converter = BasicBeanConverter.builder()
+                               .defaultSettings()
+                               .addSetting("mapEntrySeparator", ": ")
+                               .build();
+                       var stringifier = Stringifiers.mapEntryStringifier();
+                       var entry = Map.entry("name", "John");
+                       var result = stringifier.apply(converter, entry);
+
+                       assertEquals("name: John", result);
+               }
+
+               @Test
+               void a05_stringifyWithComplexValues() {
+                       var converter = 
BasicBeanConverter.builder().defaultSettings().build();
+                       var stringifier = Stringifiers.mapEntryStringifier();
+                       var entry = Map.entry("list", List.of("a", "b"));
+                       var result = stringifier.apply(converter, entry);
+
+                       assertEquals("list=[a,b]", result);
+               }
+       }
+
+       @Nested
+       class b_CalendarStringifier {
+
+               @Test
+               void b01_stringifyGregorianCalendar() {
+                       var converter = 
BasicBeanConverter.builder().defaultSettings().build();
+                       var stringifier = Stringifiers.calendarStringifier();
+                       var calendar = new GregorianCalendar(2023, 
Calendar.JANUARY, 15, 10, 30, 0);
+                       calendar.setTimeZone(TimeZone.getTimeZone("UTC"));
+                       var result = stringifier.apply(converter, calendar);
+
+                       assertMatchesGlob("2023-01-15T*", result);
+               }
+
+               @Test
+               void b02_stringifyWithCustomFormat() {
+                       var converter = BasicBeanConverter.builder()
+                               .defaultSettings()
+                               .addSetting("calendarFormat", 
DateTimeFormatter.ISO_LOCAL_DATE)
+                               .build();
+                       var stringifier = Stringifiers.calendarStringifier();
+                       var calendar = new GregorianCalendar(2023, 
Calendar.DECEMBER, 25);
+                       var result = stringifier.apply(converter, calendar);
+
+                       assertMatchesGlob("2023-12-25", result);
+               }
+       }
+
+       @Nested
+       class c_DateStringifier {
+
+               @Test
+               void c01_stringifyDate() {
+                       var stringifier = Stringifiers.dateStringifier();
+                       var date = new Date(1673780400000L); // 
2023-01-15T11:00:00Z
+                       var result = stringifier.apply(null, date);
+
+                       assertEquals("2023-01-15T11:00:00Z", result);
+               }
+
+               @Test
+               void c02_stringifyDateWithConverter() {
+                       var converter = 
BasicBeanConverter.builder().defaultSettings().build();
+                       var stringifier = Stringifiers.dateStringifier();
+                       var date = new Date(0); // Epoch
+                       var result = stringifier.apply(converter, date);
+
+                       assertEquals("1970-01-01T00:00:00Z", result);
+               }
+       }
+
+       @Nested
+       class d_InputStreamStringifier {
+
+               @Test
+               void d01_stringifyInputStream() {
+                       var stringifier = Stringifiers.inputStreamStringifier();
+                       var input = new ByteArrayInputStream(new byte[]{0x48, 
0x65, 0x6C, 0x6C, 0x6F}); // "Hello"
+                       var result = stringifier.apply(null, input);
+
+                       assertEquals("48656C6C6F", result);
+               }
+
+               @Test
+               void d02_stringifyEmptyInputStream() {
+                       var stringifier = Stringifiers.inputStreamStringifier();
+                       var input = new ByteArrayInputStream(new byte[0]);
+                       var result = stringifier.apply(null, input);
+
+                       assertEquals("", result);
+               }
+
+               @Test
+               void d03_stringifyInputStreamWithZeros() {
+                       var stringifier = Stringifiers.inputStreamStringifier();
+                       var input = new ByteArrayInputStream(new byte[]{0x00, 
(byte)0xFF, 0x7F});
+                       var result = stringifier.apply(null, input);
+
+                       assertEquals("00FF7F", result);
+               }
+       }
+
+       @Nested
+       class e_ByteArrayStringifier {
+
+               @Test
+               void e01_stringifyByteArray() {
+                       var stringifier = Stringifiers.byteArrayStringifier();
+                       var input = new byte[]{0x48, 0x65, 0x6C, 0x6C, 0x6F}; 
// "Hello"
+                       var result = stringifier.apply(null, input);
+
+                       assertEquals("48656C6C6F", result);
+               }
+
+               @Test
+               void e02_stringifyEmptyByteArray() {
+                       var stringifier = Stringifiers.byteArrayStringifier();
+                       var input = new byte[0];
+                       var result = stringifier.apply(null, input);
+
+                       assertEquals("", result);
+               }
+
+               @Test
+               void e03_stringifyByteArrayWithAllValues() {
+                       var stringifier = Stringifiers.byteArrayStringifier();
+                       var input = new byte[]{0x00, (byte)0xFF, 0x7F, 
(byte)0x80};
+                       var result = stringifier.apply(null, input);
+
+                       assertEquals("00FF7F80", result);
+               }
+       }
+
+       @Nested
+       class f_ReaderStringifier {
+
+               @ParameterizedTest
+               @ValueSource(strings = {
+                       "Hello World",
+                       "",
+                       "Line 1\nLine 2\nLine 3"
+               })
+               void f01_stringifyReader(String input) {
+                       var stringifier = Stringifiers.readerStringifier();
+                       var reader = new StringReader(input);
+                       var result = stringifier.apply(null, reader);
+
+                       assertEquals(input, result);
+               }
+       }
+
+       @Nested
+       class g_FileStringifier {
+
+               @Test
+               void g01_stringifyTempFile() throws Exception {
+                       var stringifier = Stringifiers.fileStringifier();
+                       var tempFile = Files.createTempFile("test", ".txt");
+                       Files.writeString(tempFile, "Test content");
+
+                       try {
+                               var result = stringifier.apply(null, 
tempFile.toFile());
+                               assertEquals("Test content", result);
+                       } finally {
+                               Files.deleteIfExists(tempFile);
+                       }
+               }
+
+               @Test
+               void g02_stringifyEmptyFile() throws Exception {
+                       var stringifier = Stringifiers.fileStringifier();
+                       var tempFile = Files.createTempFile("empty", ".txt");
+
+                       try {
+                               var result = stringifier.apply(null, 
tempFile.toFile());
+                               assertEquals("", result);
+                       } finally {
+                               Files.deleteIfExists(tempFile);
+                       }
+               }
+       }
+
+       @Nested
+       class h_EnumStringifier {
+
+               enum TestEnum { VALUE1, VALUE2, LONG_VALUE_NAME }
+
+               @Test
+               void h01_stringifyEnum() {
+                       var stringifier = Stringifiers.enumStringifier();
+                       var result = stringifier.apply(null, TestEnum.VALUE1);
+
+                       assertEquals("VALUE1", result);
+               }
+
+               @Test
+               void h02_stringifyEnumWithLongName() {
+                       var stringifier = Stringifiers.enumStringifier();
+                       var result = stringifier.apply(null, 
TestEnum.LONG_VALUE_NAME);
+
+                       assertEquals("LONG_VALUE_NAME", result);
+               }
+
+               @Test
+               void h03_stringifyStandardEnum() {
+                       var stringifier = Stringifiers.enumStringifier();
+                       var result = stringifier.apply(null, 
Thread.State.RUNNABLE);
+
+                       assertEquals("RUNNABLE", result);
+               }
+       }
+
+       @Nested
+       class i_ClassStringifier {
+
+               @Test
+               void i01_stringifyClassSimple() {
+                       var converter = BasicBeanConverter.builder()
+                               .defaultSettings()
+                               .addSetting("classNameFormat", "simple")
+                               .build();
+                       var stringifier = Stringifiers.classStringifier();
+                       var result = stringifier.apply(converter, String.class);
+
+                       assertEquals("String", result);
+               }
+
+               @Test
+               void i02_stringifyClassCanonical() {
+                       var converter = BasicBeanConverter.builder()
+                               .defaultSettings()
+                               .addSetting("classNameFormat", "canonical")
+                               .build();
+                       var stringifier = Stringifiers.classStringifier();
+                       var result = stringifier.apply(converter, 
ArrayList.class);
+
+                       assertEquals("java.util.ArrayList", result);
+               }
+
+               @Test
+               void i03_stringifyClassFull() {
+                       var converter = BasicBeanConverter.builder()
+                               .defaultSettings()
+                               .addSetting("classNameFormat", "full")
+                               .build();
+                       var stringifier = Stringifiers.classStringifier();
+                       var result = stringifier.apply(converter, 
HashMap.class);
+
+                       assertEquals("java.util.HashMap", result);
+               }
+
+               @Test
+               void i04_stringifyPrimitiveClass() {
+                       var converter = 
BasicBeanConverter.builder().defaultSettings().build();
+                       var stringifier = Stringifiers.classStringifier();
+                       var result = stringifier.apply(converter, int.class);
+
+                       assertEquals("int", result);
+               }
+       }
+
+       @Nested
+       class j_ConstructorStringifier {
+
+               @Test
+               void j01_stringifyNoArgConstructor() throws Exception {
+                       var converter = 
BasicBeanConverter.builder().defaultSettings().build();
+                       var stringifier = Stringifiers.constructorStringifier();
+                       var constructor = ArrayList.class.getConstructor();
+                       var result = stringifier.apply(converter, constructor);
+
+                       assertEquals("ArrayList()", result);
+               }
+
+               @Test
+               void j02_stringifyParameterizedConstructor() throws Exception {
+                       var converter = 
BasicBeanConverter.builder().defaultSettings().build();
+                       var stringifier = Stringifiers.constructorStringifier();
+                       var constructor = 
String.class.getConstructor(char[].class);
+                       var result = stringifier.apply(converter, constructor);
+
+                       assertEquals("String(char[])", result);
+               }
+
+               @Test
+               void j03_stringifyMultiParameterConstructor() throws Exception {
+                       var converter = 
BasicBeanConverter.builder().defaultSettings().build();
+                       var stringifier = Stringifiers.constructorStringifier();
+                       var constructor = 
String.class.getConstructor(byte[].class, int.class, int.class);
+                       var result = stringifier.apply(converter, constructor);
+
+                       assertEquals("String(byte[],int,int)", result);
+               }
+       }
+
+       @Nested
+       class k_MethodStringifier {
+
+               @Test
+               void k01_stringifyNoArgMethod() throws Exception {
+                       var converter = 
BasicBeanConverter.builder().defaultSettings().build();
+                       var stringifier = Stringifiers.methodStringifier();
+                       var method = Object.class.getMethod("toString");
+                       var result = stringifier.apply(converter, method);
+
+                       assertEquals("toString()", result);
+               }
+
+               @Test
+               void k02_stringifyParameterizedMethod() throws Exception {
+                       var converter = 
BasicBeanConverter.builder().defaultSettings().build();
+                       var stringifier = Stringifiers.methodStringifier();
+                       var method = String.class.getMethod("substring", 
int.class);
+                       var result = stringifier.apply(converter, method);
+
+                       assertEquals("substring(int)", result);
+               }
+
+               @Test
+               void k03_stringifyMultiParameterMethod() throws Exception {
+                       var converter = 
BasicBeanConverter.builder().defaultSettings().build();
+                       var stringifier = Stringifiers.methodStringifier();
+                       var method = String.class.getMethod("substring", 
int.class, int.class);
+                       var result = stringifier.apply(converter, method);
+
+                       assertEquals("substring(int,int)", result);
+               }
+       }
+
+       @Nested
+       class l_ListStringifier {
+
+               @Test
+               void l01_stringifyList() {
+                       var converter = 
BasicBeanConverter.builder().defaultSettings().build();
+                       var stringifier = Stringifiers.listStringifier();
+                       var input = List.of("a", "b", "c");
+                       var result = stringifier.apply(converter, input);
+
+                       assertEquals("[a,b,c]", result);
+               }
+
+               @Test
+               void l02_stringifyEmptyList() {
+                       var converter = 
BasicBeanConverter.builder().defaultSettings().build();
+                       var stringifier = Stringifiers.listStringifier();
+                       var input = List.of();
+                       var result = stringifier.apply(converter, input);
+
+                       assertEquals("[]", result);
+               }
+
+               @Test
+               void l03_stringifyWithCustomDelimiters() {
+                       var converter = BasicBeanConverter.builder()
+                               .defaultSettings()
+                               .addSetting("fieldSeparator", "; ")
+                               .addSetting("collectionPrefix", "(")
+                               .addSetting("collectionSuffix", ")")
+                               .build();
+                       var stringifier = Stringifiers.listStringifier();
+                       var input = List.of("x", "y", "z");
+                       var result = stringifier.apply(converter, input);
+
+                       assertEquals("(x; y; z)", result);
+               }
+
+               @Test
+               void l04_stringifyWithNullElements() {
+                       var converter = 
BasicBeanConverter.builder().defaultSettings().build();
+                       var stringifier = Stringifiers.listStringifier();
+                       var input = Arrays.asList("a", null, "c");
+                       var result = stringifier.apply(converter, input);
+
+                       assertEquals("[a,<null>,c]", result);
+               }
+       }
+
+       @Nested
+       class m_MapStringifier {
+
+               @Test
+               void m01_stringifyMap() {
+                       var converter = 
BasicBeanConverter.builder().defaultSettings().build();
+                       var stringifier = Stringifiers.mapStringifier();
+                       var input = Map.of("key1", "value1", "key2", "value2");
+                       var result = stringifier.apply(converter, input);
+
+                       // Order may vary, so check both possibilities
+                       assertTrue(result.equals("{key1=value1,key2=value2}") ||
+                                          
result.equals("{key2=value2,key1=value1}"));
+               }
+
+               @Test
+               void m02_stringifyEmptyMap() {
+                       var converter = 
BasicBeanConverter.builder().defaultSettings().build();
+                       var stringifier = Stringifiers.mapStringifier();
+                       var input = Map.of();
+                       var result = stringifier.apply(converter, input);
+
+                       assertEquals("{}", result);
+               }
+
+               @Test
+               void m03_stringifyWithCustomDelimiters() {
+                       var converter = BasicBeanConverter.builder()
+                               .defaultSettings()
+                               .addSetting("fieldSeparator", "; ")
+                               .addSetting("mapPrefix", "<")
+                               .addSetting("mapSuffix", ">")
+                               .build();
+                       var stringifier = Stringifiers.mapStringifier();
+                       var input = Map.of("a", "1");
+                       var result = stringifier.apply(converter, input);
+
+                       assertEquals("<a=1>", result);
+               }
+
+               @Test
+               void m04_stringifyWithNullValues() {
+                       var converter = 
BasicBeanConverter.builder().defaultSettings().build();
+                       var stringifier = Stringifiers.mapStringifier();
+                       var input = new HashMap<String, String>();
+                       input.put("key1", "value1");
+                       input.put("key2", null);
+                       var result = stringifier.apply(converter, input);
+
+                       assertTrue(result.contains("key1=value1"));
+                       assertTrue(result.contains("key2=<null>"));
+               }
+       }
+
+       @Nested
+       class n_Integration {
+
+               @Test
+               void n01_useInBasicBeanConverter() {
+                       // Test various stringifiable objects
+                       assertBean(Map.entry("k", "v"), "<self>", "k=v");
+                       assertBean(List.of("a", "b"), "<self>", "[a,b]");
+                       assertBean(Map.of("x", "y"), "<self>", "{x=y}");
+                       assertBean(TestEnum.VALUE1, "<self>", "VALUE1");
+                       assertBean(String.class, "<self>", "String");
+               }
+
+               @Test
+               void n02_customStringifierRegistration() {
+                       // Test that custom registration works
+                       assertBean(List.of("custom"), "<self>", "[custom]");
+               }
+
+               @Test
+               void n03_stringifierChaining() {
+                       // Map with list values
+                       var complexMap = Map.of("items", List.of("a", "b"));
+                       assertMatchesGlob("*items=[a,b]*", 
BasicBeanConverter.DEFAULT.stringify(complexMap));
+               }
+
+               private enum TestEnum { VALUE1, VALUE2 }
+       }
+}
diff --git a/juneau-utest/src/test/java/org/apache/juneau/junit/Swappers.java 
b/juneau-utest/src/test/java/org/apache/juneau/junit/Swappers.java
new file mode 100644
index 000000000..f3829273d
--- /dev/null
+++ b/juneau-utest/src/test/java/org/apache/juneau/junit/Swappers.java
@@ -0,0 +1,213 @@
+// 
***************************************************************************************************************************
+// * 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.juneau.junit;
+
+import java.util.*;
+import java.util.concurrent.*;
+import java.util.function.*;
+
+/**
+ * Collection of standard swapper implementations for the Bean-Centric Testing 
framework.
+ *
+ * <p>This class provides built-in object transformation strategies that 
handle common wrapper
+ * types and asynchronous objects. These swappers are automatically registered 
when using
+ * {@link BasicBeanConverter.Builder#defaultSettings()}.</p>
+ *
+ * <h5 class='section'>Purpose:</h5>
+ * <p>Swappers pre-process objects before stringification or listification, 
enabling the converter
+ * to handle wrapper types, lazy evaluation objects, and asynchronous results 
consistently. They
+ * transform objects into more primitive forms that can be easily converted to 
strings or lists.</p>
+ *
+ * <h5 class='section'>Built-in Swappers:</h5>
+ * <ul>
+ *     <li><b>{@link #optionalSwapper()}</b> - Unwraps {@link Optional} values 
to their contained objects or <jk>null</jk></li>
+ *     <li><b>{@link #supplierSwapper()}</b> - Evaluates {@link Supplier} 
functions to get their supplied values</li>
+ *     <li><b>{@link #futureSwapper()}</b> - Extracts completed {@link Future} 
results or returns status messages</li>
+ * </ul>
+ *
+ * <h5 class='section'>Usage Example:</h5>
+ * <p class='bjava'>
+ *     <jc>// Register swappers using builder</jc>
+ *     <jk>var</jk> <jv>converter</jv> = 
BasicBeanConverter.<jsm>builder</jsm>()
+ *             .defaultSettings()
+ *             .addSwapper(Optional.<jk>class</jk>, 
Swappers.<jsm>optionalSwapper</jsm>())
+ *             .addSwapper(Supplier.<jk>class</jk>, 
Swappers.<jsm>supplierSwapper</jsm>())
+ *             .addSwapper(Future.<jk>class</jk>, 
Swappers.<jsm>futureSwapper</jsm>())
+ *             .build();
+ * </p>
+ *
+ * <h5 class='section'>Custom Swapper Development:</h5>
+ * <p>When creating custom swappers, follow these patterns:</p>
+ * <ul>
+ *     <li><b>Null Safety:</b> Handle <jk>null</jk> inputs gracefully</li>
+ *     <li><b>Exception Handling:</b> Convert exceptions to meaningful error 
messages</li>
+ *     <li><b>Status Indication:</b> Provide clear status for incomplete or 
error states</li>
+ *     <li><b>Non-blocking:</b> Avoid blocking operations that might hang 
tests</li>
+ * </ul>
+ *
+ * @see Swapper
+ * @see BasicBeanConverter.Builder#addSwapper(Class, Swapper)
+ * @see BasicBeanConverter.Builder#defaultSettings()
+ */
+@SuppressWarnings({"unchecked","rawtypes"})
+public class Swappers {
+
+       /**
+        * Constructor.
+        */
+       private Swappers() {}
+
+       /**
+        * Returns a swapper for {@link Optional} objects that unwraps them to 
their contained values.
+        *
+        * <p>This swapper extracts the value from Optional instances, 
returning the contained object
+        * if present, or <jk>null</jk> if the Optional is empty. This allows 
Optional-wrapped values
+        * to be processed naturally by the converter without special 
handling.</p>
+        *
+        * <h5 class='section'>Behavior:</h5>
+        * <ul>
+        *      <li><b>Present value:</b> Returns the contained object for 
further processing</li>
+        *      <li><b>Empty Optional:</b> Returns <jk>null</jk>, which will be 
rendered as the configured null value</li>
+        * </ul>
+        *
+        * <h5 class='section'>Usage Examples:</h5>
+        * <p class='bjava'>
+        *      <jc>// Test Optional with value</jc>
+        *      <jk>var</jk> <jv>optional</jv> = 
Optional.<jsm>of</jsm>(<js>"Hello"</js>);
+        *      <jsm>assertBean</jsm>(<jv>optional</jv>, 
<js>"&lt;self&gt;"</js>, <js>"Hello"</js>);
+        *
+        *      <jc>// Test empty Optional</jc>
+        *      <jk>var</jk> <jv>empty</jv> = Optional.<jsm>empty</jsm>();
+        *      <jsm>assertBean</jsm>(<jv>empty</jv>, <js>"&lt;self&gt;"</js>, 
<js>"&lt;null&gt;"</js>);
+        *
+        *      <jc>// Test nested Optional in object</jc>
+        *      <jk>var</jk> <jv>user</jv> = <jk>new</jk> 
User().setName(Optional.<jsm>of</jsm>(<js>"John"</js>));
+        *      <jsm>assertBean</jsm>(<jv>user</jv>, <js>"name"</js>, 
<js>"John"</js>);
+        * </p>
+        *
+        * <h5 class='section'>Integration:</h5>
+        * <p>This swapper is particularly useful when working with modern Java 
APIs that return
+        * Optional values. It allows test assertions to focus on the actual 
data rather than
+        * Optional wrapper mechanics.</p>
+        *
+        * @return A {@link Swapper} for {@link Optional} objects
+        * @see Optional
+        */
+       public static Swapper<Optional> optionalSwapper() {
+               return (bc, optional) -> optional.orElse(null);
+       }
+
+       /**
+        * Returns a swapper for {@link Supplier} objects that evaluates them 
to get their supplied values.
+        *
+        * <p>This swapper calls the {@link Supplier#get()} method to obtain 
the value that the supplier
+        * provides. This enables testing of lazy-evaluated or 
dynamically-computed values as if they
+        * were regular objects.</p>
+        *
+        * <h5 class='section'>Behavior:</h5>
+        * <ul>
+        *      <li><b>Successful evaluation:</b> Returns the result of calling 
{@link Supplier#get()}</li>
+        *      <li><b>Exception during evaluation:</b> Allows the exception to 
propagate (no special handling)</li>
+        * </ul>
+        *
+        * <h5 class='section'>Usage Examples:</h5>
+        * <p class='bjava'>
+        *      <jc>// Test supplier with simple value</jc>
+        *      <jk>var</jk> <jv>supplier</jv> = () -&gt; <js>"Hello 
World"</js>;
+        *      <jsm>assertBean</jsm>(<jv>supplier</jv>, 
<js>"&lt;self&gt;"</js>, <js>"Hello World"</js>);
+        *
+        *      <jc>// Test supplier in object property</jc>
+        *      <jk>var</jk> <jv>config</jv> = <jk>new</jk> 
Configuration().setDynamicValue(() -&gt; calculateValue());
+        *      <jsm>assertBean</jsm>(<jv>config</jv>, <js>"dynamicValue"</js>, 
<js>"computed-result"</js>);
+        *
+        *      <jc>// Test supplier returning null</jc>
+        *      <jk>var</jk> <jv>nullSupplier</jv> = () -&gt; <jk>null</jk>;
+        *      <jsm>assertBean</jsm>(<jv>nullSupplier</jv>, 
<js>"&lt;self&gt;"</js>, <js>"&lt;null&gt;"</js>);
+        * </p>
+        *
+        * <h5 class='section'>Important Notes:</h5>
+        * <ul>
+        *      <li><b>Side Effects:</b> The supplier will be evaluated during 
conversion, which may cause side effects</li>
+        *      <li><b>Performance:</b> Expensive computations in suppliers 
will impact test performance</li>
+        *      <li><b>Exception Handling:</b> Exceptions from suppliers are 
not caught by this swapper</li>
+        * </ul>
+        *
+        * @return A {@link Swapper} for {@link Supplier} objects
+        * @see Supplier
+        */
+       public static Swapper<Supplier> supplierSwapper() {
+               return (bc, supplier) -> supplier.get();
+       }
+
+       /**
+        * Returns a swapper for {@link Future} objects that extracts completed 
results or status information.
+        *
+        * <p>This swapper handles {@link Future} objects in a non-blocking 
manner, providing meaningful
+        * information about the future's state without waiting for 
completion:</p>
+        *
+        * <h5 class='section'>Behavior:</h5>
+        * <ul>
+        *      <li><b>Completed successfully:</b> Returns the actual result 
value</li>
+        *      <li><b>Completed with exception:</b> Returns <js>"&lt;error: 
{message}&gt;"</js> format</li>
+        *      <li><b>Cancelled:</b> Returns <js>"&lt;cancelled&gt;"</js></li>
+        *      <li><b>Still pending:</b> Returns 
<js>"&lt;pending&gt;"</js></li>
+        * </ul>
+        *
+        * <h5 class='section'>Usage Examples:</h5>
+        * <p class='bjava'>
+        *      <jc>// Test completed future</jc>
+        *      <jk>var</jk> <jv>future</jv> = 
CompletableFuture.<jsm>completedFuture</jsm>(<js>"Hello"</js>);
+        *      <jsm>assertBean</jsm>(<jv>future</jv>, <js>"&lt;self&gt;"</js>, 
<js>"Hello"</js>);
+        *
+        *      <jc>// Test pending future</jc>
+        *      <jk>var</jk> <jv>pending</jv> = <jk>new</jk> 
CompletableFuture&lt;String&gt;();
+        *      <jsm>assertBean</jsm>(<jv>pending</jv>, 
<js>"&lt;self&gt;"</js>, <js>"&lt;pending&gt;"</js>);
+        *
+        *      <jc>// Test cancelled future</jc>
+        *      <jk>var</jk> <jv>cancelled</jv> = <jk>new</jk> 
CompletableFuture&lt;String&gt;();
+        *      <jv>cancelled</jv>.cancel(<jk>true</jk>);
+        *      <jsm>assertBean</jsm>(<jv>cancelled</jv>, 
<js>"&lt;self&gt;"</js>, <js>"&lt;cancelled&gt;"</js>);
+        *
+        *      <jc>// Test failed future</jc>
+        *      <jk>var</jk> <jv>failed</jv> = <jk>new</jk> 
CompletableFuture&lt;String&gt;();
+        *      <jv>failed</jv>.completeExceptionally(<jk>new</jk> 
RuntimeException(<js>"Test error"</js>));
+        *      <jsm>assertBean</jsm>(<jv>failed</jv>, <js>"&lt;self&gt;"</js>, 
<js>"&lt;error: Test error&gt;"</js>);
+        * </p>
+        *
+        * <h5 class='section'>Non-blocking Guarantee:</h5>
+        * <p>This swapper never calls {@link Future#get()} without first 
checking {@link Future#isDone()},
+        * ensuring that test execution is never blocked by incomplete futures. 
This makes it safe to use
+        * in unit tests without risking hangs or timeouts.</p>
+        *
+        * <h5 class='section'>Error Handling:</h5>
+        * <p>When a future completes exceptionally, the swapper extracts the 
exception message and
+        * formats it as <js>"&lt;error: {message}&gt;"</js>. This provides 
useful debugging information
+        * while maintaining a consistent string format for assertions.</p>
+        *
+        * @return A {@link Swapper} for {@link Future} objects
+        * @see Future
+        * @see CompletableFuture
+        */
+       public static Swapper<Future> futureSwapper() {
+               return (bc, future) -> {
+                       if (future.isDone() && !future.isCancelled()) {
+                               try {
+                                       return future.get();
+                               } catch (Exception e) {
+                                       return "<error: " + e.getMessage() + 
">";
+                               }
+                       }
+                       return future.isCancelled() ? "<cancelled>" : 
"<pending>";
+               };
+       }
+}
diff --git 
a/juneau-utest/src/test/java/org/apache/juneau/junit/Swappers_Test.java 
b/juneau-utest/src/test/java/org/apache/juneau/junit/Swappers_Test.java
new file mode 100644
index 000000000..4f17079b3
--- /dev/null
+++ b/juneau-utest/src/test/java/org/apache/juneau/junit/Swappers_Test.java
@@ -0,0 +1,227 @@
+// 
***************************************************************************************************************************
+// * 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.juneau.junit;
+
+import static org.apache.juneau.junit.Assertions2.*;
+import static org.junit.jupiter.api.Assertions.*;
+
+import java.util.*;
+import java.util.concurrent.*;
+import java.util.function.*;
+
+import org.junit.jupiter.api.*;
+
+/**
+ * Unit tests for {@link Swappers}.
+ */
+class Swappers_Test extends TestBase {
+
+       @Nested
+       class a_OptionalSwapper {
+
+               @Test
+               void a01_swapPresentOptional() {
+                       var swapper = Swappers.optionalSwapper();
+                       var result = swapper.apply(null, Optional.of("Hello"));
+                       assertEquals("Hello", result);
+               }
+
+               @Test
+               void a02_swapEmptyOptional() {
+                       var swapper = Swappers.optionalSwapper();
+                       var result = swapper.apply(null, Optional.empty());
+                       assertNull(result);
+               }
+
+               @Test
+               void a03_swapOptionalWithNull() {
+                       var swapper = Swappers.optionalSwapper();
+                       var result = swapper.apply(null, 
Optional.ofNullable(null));
+                       assertNull(result);
+               }
+
+               @Test
+               void a04_swapOptionalWithComplexObject() {
+                       var swapper = Swappers.optionalSwapper();
+                       var list = List.of("a", "b", "c");
+                       var result = swapper.apply(null, Optional.of(list));
+                       assertSame(list, result);
+               }
+
+               @Test
+               void a05_swapOptionalWithConverter() {
+                       var converter = 
BasicBeanConverter.builder().defaultSettings().build();
+                       var swapper = Swappers.optionalSwapper();
+                       var result = swapper.apply(converter, Optional.of(42));
+                       assertEquals(42, result);
+               }
+       }
+
+       @Nested
+       class b_SupplierSwapper {
+
+               @Test
+               void b01_swapSupplierWithValue() {
+                       var swapper = Swappers.supplierSwapper();
+                       Supplier<String> supplier = () -> "Hello World";
+                       var result = swapper.apply(null, supplier);
+                       assertEquals("Hello World", result);
+               }
+
+               @Test
+               void b02_swapSupplierWithNull() {
+                       var swapper = Swappers.supplierSwapper();
+                       Supplier<String> supplier = () -> null;
+                       var result = swapper.apply(null, supplier);
+                       assertNull(result);
+               }
+
+               @Test
+               void b03_swapSupplierWithComplexObject() {
+                       var swapper = Swappers.supplierSwapper();
+                       var list = List.of("x", "y", "z");
+                       Supplier<List<String>> supplier = () -> list;
+                       var result = swapper.apply(null, supplier);
+                       assertSame(list, result);
+               }
+
+               @Test
+               void b04_swapSupplierWithConverter() {
+                       var converter = 
BasicBeanConverter.builder().defaultSettings().build();
+                       var swapper = Swappers.supplierSwapper();
+                       Supplier<Integer> supplier = () -> 123;
+                       var result = swapper.apply(converter, supplier);
+                       assertEquals(123, result);
+               }
+
+               @Test
+               void b05_swapSupplierWithException() {
+                       var swapper = Swappers.supplierSwapper();
+                       Supplier<String> supplier = () -> {
+                               throw new RuntimeException("Test exception");
+                       };
+                       assertThrows(RuntimeException.class, () -> 
swapper.apply(null, supplier));
+               }
+       }
+
+       @Nested
+       class c_FutureSwapper {
+
+               @Test
+               void c01_swapCompletedFuture() {
+                       var swapper = Swappers.futureSwapper();
+                       var future = 
CompletableFuture.completedFuture("Result");
+                       var result = swapper.apply(null, future);
+                       assertEquals("Result", result);
+               }
+
+               @Test
+               void c02_swapPendingFuture() {
+                       var swapper = Swappers.futureSwapper();
+                       var future = new CompletableFuture<String>();
+                       var result = swapper.apply(null, future);
+                       assertEquals("<pending>", result);
+               }
+
+               @Test
+               void c03_swapCancelledFuture() {
+                       var swapper = Swappers.futureSwapper();
+                       var future = new CompletableFuture<String>();
+                       future.cancel(true);
+                       var result = swapper.apply(null, future);
+                       assertEquals("<cancelled>", result);
+               }
+
+               @Test
+               void c04_swapFailedFuture() {
+                       var swapper = Swappers.futureSwapper();
+                       var future = new CompletableFuture<String>();
+                       future.completeExceptionally(new RuntimeException("Test 
error"));
+                       var result = swapper.apply(null, future);
+                       assertEquals("<error: java.lang.RuntimeException: Test 
error>", result);
+               }
+
+               @Test
+               void c05_swapCompletedFutureWithNull() {
+                       var swapper = Swappers.futureSwapper();
+                       var future = CompletableFuture.completedFuture(null);
+                       var result = swapper.apply(null, future);
+                       assertNull(result);
+               }
+
+               @Test
+               void c06_swapFutureWithComplexObject() {
+                       var swapper = Swappers.futureSwapper();
+                       var list = List.of("a", "b", "c");
+                       var future = CompletableFuture.completedFuture(list);
+                       var result = swapper.apply(null, future);
+                       assertSame(list, result);
+               }
+
+               @Test
+               void c07_swapFutureWithConverter() {
+                       var converter = 
BasicBeanConverter.builder().defaultSettings().build();
+                       var swapper = Swappers.futureSwapper();
+                       var future = CompletableFuture.completedFuture(999);
+                       var result = swapper.apply(converter, future);
+                       assertEquals(999, result);
+               }
+
+               @Test
+               void c08_swapFutureWithNestedCause() {
+                       var swapper = Swappers.futureSwapper();
+                       var future = new CompletableFuture<String>();
+                       var rootCause = new IllegalArgumentException("Root 
cause");
+                       var wrappedException = new RuntimeException("Wrapper", 
rootCause);
+                       future.completeExceptionally(wrappedException);
+                       var result = swapper.apply(null, future);
+                       assertEquals("<error: java.lang.RuntimeException: 
Wrapper>", result);
+               }
+       }
+
+       @Nested
+       class d_Integration {
+
+               @Test
+               void d01_useInBasicBeanConverter() {
+                       // Test Optional swapping
+                       assertBean(Optional.of("test"), "<self>", "test");
+                       assertBean(Optional.empty(), "<self>", "<null>");
+
+                       // Test Supplier swapping
+                       Supplier<String> supplier = () -> "supplied";
+                       assertBean(supplier, "<self>", "supplied");
+
+                       // Test Future swapping
+                       var future = 
CompletableFuture.completedFuture("future-result");
+                       assertBean(future, "<self>", "future-result");
+               }
+
+               @Test
+               void d02_customSwapperRegistration() {
+                       // Test that custom registration works
+                       assertBean(Optional.of("custom"), "<self>", "custom");
+               }
+
+               @Test
+               void d03_nestedSwapping() {
+                       // Test nested Optional in Supplier
+                       Supplier<Optional<String>> nestedSupplier = () -> 
Optional.of("nested");
+                       assertBean(nestedSupplier, "<self>", "nested");
+
+                       // Test Optional containing Supplier
+                       Supplier<String> supplier = () -> "inner";
+                       assertBean(Optional.of(supplier), "<self>", "inner");
+               }
+       }
+}
diff --git a/juneau-utest/src/test/java/org/apache/juneau/junit/Utils.java 
b/juneau-utest/src/test/java/org/apache/juneau/junit/Utils.java
index 782d040cc..1cba4f1da 100644
--- a/juneau-utest/src/test/java/org/apache/juneau/junit/Utils.java
+++ b/juneau-utest/src/test/java/org/apache/juneau/junit/Utils.java
@@ -48,7 +48,7 @@ import org.opentest4j.*;
  *   String <jv>validated</jv> = 
<jsm>assertArgNotNull</jsm>(<js>"username"</js>, <jv>username</jv>);
  *
  *   <jc>// Pattern matching</jc>
- *   Pattern <jv>pattern</jv> = 
<jsm>getMatchPattern</jsm>(<js>"user_*_temp"</js>);
+ *   Pattern <jv>pattern</jv> = 
<jsm>getGlobMatchPattern</jsm>(<js>"user_*_temp"</js>);
  * </p>
  *
  * <p><b>Thread Safety:</b> All methods in this class are thread-safe as they 
are stateless static methods.
@@ -303,7 +303,7 @@ class Utils {
        }
 
        /**
-        * Converts a string containing wildcard characters to a regular 
expression {@link Pattern}.
+        * Converts a string containing glob-style wildcard characters to a 
regular expression {@link Pattern}.
         *
         * <p>This method converts glob-style patterns to regular expressions 
with the following mappings:
         * <ul>
@@ -314,21 +314,21 @@ class Utils {
         *
         * <h5 class='section'>Example:</h5>
         * <p class='bjava'>
-        *   <jk>var</jk> <jv>pattern</jv> = 
<jsm>getMatchPattern</jsm>(<js>"user_*_temp"</js>);
+        *   <jk>var</jk> <jv>pattern</jv> = 
<jsm>getGlobMatchPattern</jsm>(<js>"user_*_temp"</js>);
         *   <jk>boolean</jk> <jv>matches</jv> = 
<jv>pattern</jv>.matcher(<js>"user_alice_temp"</js>).matches();  <jc>// 
true</jc>
         *   <jv>matches</jv> = 
<jv>pattern</jv>.matcher(<js>"user_bob_temp"</js>).matches();    <jc>// 
true</jc>
         *   <jv>matches</jv> = 
<jv>pattern</jv>.matcher(<js>"admin_alice_temp"</js>).matches(); <jc>// 
false</jc>
         * </p>
         *
-        * @param s The wildcard pattern string.
+        * @param s The glob-style wildcard pattern string.
         * @return A compiled {@link Pattern} object, or null if the input 
string is null.
         */
-       public static Pattern getMatchPattern(String s) {
-               return getMatchPattern(s, 0);
+       public static Pattern getGlobMatchPattern(String s) {
+               return getGlobMatchPattern(s, 0);
        }
 
        /**
-        * Converts a string containing wildcard characters to a regular 
expression {@link Pattern} with flags.
+        * Converts a string containing glob-style wildcard characters to a 
regular expression {@link Pattern} with flags.
         *
         * <p>This method converts glob-style patterns to regular expressions 
with the following mappings:
         * <ul>
@@ -340,15 +340,15 @@ class Utils {
         * <h5 class='section'>Example:</h5>
         * <p class='bjava'>
         *   <jc>// Case-insensitive matching</jc>
-        *   <jk>var</jk> <jv>pattern</jv> = 
<jsm>getMatchPattern</jsm>(<js>"USER_*"</js>, 
Pattern.<jsf>CASE_INSENSITIVE</jsf>);
+        *   <jk>var</jk> <jv>pattern</jv> = 
<jsm>getGlobMatchPattern</jsm>(<js>"USER_*"</js>, 
Pattern.<jsf>CASE_INSENSITIVE</jsf>);
         *   <jk>boolean</jk> <jv>matches</jv> = 
<jv>pattern</jv>.matcher(<js>"user_alice"</js>).matches();  <jc>// true</jc>
         * </p>
         *
-        * @param s The wildcard pattern string.
+        * @param s The glob-style wildcard pattern string.
         * @param flags Regular expression flags (see {@link Pattern} 
constants).
         * @return A compiled {@link Pattern} object, or null if the input 
string is null.
         */
-       public static Pattern getMatchPattern(String s, int flags) {
+       public static Pattern getGlobMatchPattern(String s, int flags) {
                if (s == null)
                        return null;
                var sb = new StringBuilder();
diff --git a/juneau-utest/src/test/java/org/apache/juneau/junit/Utils_Test.java 
b/juneau-utest/src/test/java/org/apache/juneau/junit/Utils_Test.java
index 7b4c3fc14..a7bb3957e 100644
--- a/juneau-utest/src/test/java/org/apache/juneau/junit/Utils_Test.java
+++ b/juneau-utest/src/test/java/org/apache/juneau/junit/Utils_Test.java
@@ -336,9 +336,25 @@ class Utils_Test extends TestBase {
                assertString(f("User {0} (age {1}) is active: {2}", "Alice", 
25, true), "User Alice (age 25) is active: true");
        }
 
+       @Test
+       @DisplayName("f() - Edge cases and special characters")
+       void f04_fEdgeCases() {
+               // Test with special characters (avoid braces in parameters for 
MessageFormat compatibility)
+               assertEquals("Pattern: .*items=\\[a,b\\].*", f("Pattern: {0}", 
".*items=\\[a,b\\].*"));
+               assertEquals("Simple: no braces here", f("Simple: {0}", "no 
braces here"));
+               assertEquals("Mixed: string with data and 42", f("Mixed: {0} 
and {1}", "string with data", 42));
+
+               // Test with non-string arguments
+               assertEquals("Number: 42", f("Number: {0}", 42));
+               assertEquals("Boolean: true", f("Boolean: {0}", true));
+
+               // Test that single quotes in input are properly handled (need 
to be doubled in MessageFormat)
+               assertEquals("Text with 'single quotes'", f("Text with {0}", 
"'single quotes'"));
+       }
+
        @Test
        @DisplayName("fs() - Supplier creation")
-       void f04_fsSupplierCreation() {
+       void f05_fsSupplierCreation() {
                var supplier = fs("User {0} has {1} items", "Bob", 3);
                assertEquals("User Bob has 3 items", supplier.get());
 
@@ -348,7 +364,7 @@ class Utils_Test extends TestBase {
 
        @Test
        @DisplayName("fs() - No parameters supplier")
-       void f05_fsNoParameters() {
+       void f06_fsNoParameters() {
                assertEquals("Static message", fs("Static message").get());
        }
 
@@ -357,9 +373,9 @@ class Utils_Test extends TestBase {
        // 
====================================================================================================
 
        @Test
-       @DisplayName("getMatchPattern() - Basic wildcard patterns")
-       void g01_getMatchPatternBasicWildcards() {
-               var pattern = getMatchPattern("user_*_temp");
+       @DisplayName("getGlobMatchPattern() - Basic wildcard patterns")
+       void g01_getGlobMatchPatternBasicWildcards() {
+               var pattern = getGlobMatchPattern("user_*_temp");
                assertTrue(pattern.matcher("user_alice_temp").matches());
                assertTrue(pattern.matcher("user_bob_temp").matches());
                assertTrue(pattern.matcher("user__temp").matches());  // Empty 
match
@@ -368,9 +384,9 @@ class Utils_Test extends TestBase {
        }
 
        @Test
-       @DisplayName("getMatchPattern() - Question mark patterns")
-       void g02_getMatchPatternQuestionMark() {
-               var pattern = getMatchPattern("file?.txt");
+       @DisplayName("getGlobMatchPattern() - Question mark patterns")
+       void g02_getGlobMatchPatternQuestionMark() {
+               var pattern = getGlobMatchPattern("file?.txt");
                assertTrue(pattern.matcher("file1.txt").matches());
                assertTrue(pattern.matcher("fileA.txt").matches());
                assertTrue(pattern.matcher("file_.txt").matches());
@@ -379,9 +395,9 @@ class Utils_Test extends TestBase {
        }
 
        @Test
-       @DisplayName("getMatchPattern() - Combined wildcards")
-       void g03_getMatchPatternCombined() {
-               var pattern = getMatchPattern("test_*.?");
+       @DisplayName("getGlobMatchPattern() - Combined wildcards")
+       void g03_getGlobMatchPatternCombined() {
+               var pattern = getGlobMatchPattern("test_*.?");
                assertTrue(pattern.matcher("test_data.1").matches());
                assertTrue(pattern.matcher("test_long_filename.x").matches());
                assertTrue(pattern.matcher("test_.a").matches());
@@ -390,48 +406,48 @@ class Utils_Test extends TestBase {
        }
 
        @Test
-       @DisplayName("getMatchPattern() - Special regex characters")
-       void g04_getMatchPatternSpecialChars() {
+       @DisplayName("getGlobMatchPattern() - Special regex characters")
+       void g04_getGlobMatchPatternSpecialChars() {
                // Test that regex special characters are properly escaped
-               var pattern = getMatchPattern("file[1].txt");
+               var pattern = getGlobMatchPattern("file[1].txt");
                assertTrue(pattern.matcher("file[1].txt").matches());
                assertFalse(pattern.matcher("file1.txt").matches());   // 
Should not match as character class
 
-               pattern = getMatchPattern("amount$100");
+               pattern = getGlobMatchPattern("amount$100");
                assertTrue(pattern.matcher("amount$100").matches());
                assertFalse(pattern.matcher("amount100").matches());   // 
Should require the $ character
        }
 
        @Test
-       @DisplayName("getMatchPattern() - With flags")
-       void g05_getMatchPatternWithFlags() {
-               var pattern = getMatchPattern("USER_*", 
Pattern.CASE_INSENSITIVE);
+       @DisplayName("getGlobMatchPattern() - With flags")
+       void g05_getGlobMatchPatternWithFlags() {
+               var pattern = getGlobMatchPattern("USER_*", 
Pattern.CASE_INSENSITIVE);
                assertTrue(pattern.matcher("user_alice").matches());
                assertTrue(pattern.matcher("USER_BOB").matches());
                assertTrue(pattern.matcher("User_Charlie").matches());
        }
 
        @Test
-       @DisplayName("getMatchPattern() - Null input")
-       void g06_getMatchPatternNull() {
-               assertNull(getMatchPattern(null));
-               assertNull(getMatchPattern(null, Pattern.CASE_INSENSITIVE));
+       @DisplayName("getGlobMatchPattern() - Null input")
+       void g06_getGlobMatchPatternNull() {
+               assertNull(getGlobMatchPattern(null));
+               assertNull(getGlobMatchPattern(null, Pattern.CASE_INSENSITIVE));
        }
 
        @Test
-       @DisplayName("getMatchPattern() - Empty and edge cases")
-       void g07_getMatchPatternEdgeCases() {
+       @DisplayName("getGlobMatchPattern() - Empty and edge cases")
+       void g07_getGlobMatchPatternEdgeCases() {
                // Empty string
-               var pattern = getMatchPattern("");
+               var pattern = getGlobMatchPattern("");
                assertTrue(pattern.matcher("").matches());
                assertFalse(pattern.matcher("a").matches());
 
                // Only wildcards
-               pattern = getMatchPattern("*");
+               pattern = getGlobMatchPattern("*");
                assertTrue(pattern.matcher("").matches());
                assertTrue(pattern.matcher("anything").matches());
 
-               pattern = getMatchPattern("?");
+               pattern = getGlobMatchPattern("?");
                assertTrue(pattern.matcher("a").matches());
                assertFalse(pattern.matcher("").matches());
                assertFalse(pattern.matcher("ab").matches());

Reply via email to