Skip to main content

Stringifiers

Stringifiers define how specific types should be converted to strings for comparison in BCT assertions. They provide flexible, customizable string representations that can be tailored to your testing needs.

Built-in Stringifiers

BCT comes with comprehensive built-in stringifiers for common Java types:

// Array types
char[] chars = {'H', 'e', 'l', 'l', 'o'};
assertString("Hello", chars); // Uses charArrayStringifier()

byte[] bytes = {0x48, 0x65, 0x6C, 0x6C, 0x6F}; // "Hello"
assertString("48656C6C6F", bytes); // Uses byteArrayStringifier()

// Date and time types
Date date = new Date(1673780400000L);
assertString("2023-01-15T11:00:00Z", date); // Uses dateStringifier()

GregorianCalendar calendar = new GregorianCalendar(2023, 0, 15);
assertMatchesGlob("2023-01-15T*", calendar); // Uses calendarStringifier()

// Collection types
List<String> list = List.of("a", "b", "c");
assertString("[a,b,c]", list); // Uses listStringifier()

Map<String, String> map = Map.of("key", "value");
assertString("{key=value}", map); // Uses mapStringifier()

// Reflection types
assertString("String", String.class); // Uses classStringifier()
assertString("toString()", Object.class.getMethod("toString")); // Uses methodStringifier()

// Enum types
assertString("RUNNABLE", Thread.State.RUNNABLE); // Uses enumStringifier()

// Stream types
InputStream input = new ByteArrayInputStream("Hello".getBytes());
assertString("48656C6C6F", input); // Uses inputStreamStringifier()

Available Built-in Stringifiers

  • charArrayStringifier() - Converts char arrays to strings
  • byteArrayStringifier() - Converts byte arrays to hex strings
  • dateStringifier() - Converts Date objects to ISO-8601 format
  • calendarStringifier() - Converts Calendar objects to ISO-8601 format
  • listStringifier() - Converts List objects to bracket format
  • mapStringifier() - Converts Map objects to brace format
  • classStringifier() - Converts Class objects to readable names
  • methodStringifier() - Converts Method objects to signatures
  • enumStringifier() - Converts Enum values to names
  • inputStreamStringifier() - Converts InputStream content to hex strings
  • readerStringifier() - Converts Reader content to strings
  • fileStringifier() - Converts File content to strings

Custom Stringifiers

Define custom stringifiers for your domain-specific types:

Basic Custom Stringifier

// Date formatting
Stringifier<LocalDateTime> dateStringifier = (conv, dt) ->
dt.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME);

// Complex object formatting
Stringifier<Order> orderStringifier = (conv, order) ->
"Order#" + order.getId() + "[" + order.getStatus() + "]";

// Registration
var converter = BasicBeanConverter.builder()
.defaultSettings()
.addStringifier(LocalDateTime.class, dateStringifier)
.addStringifier(Order.class, orderStringifier)
.build();

Advanced Stringifier Example

// Money type formatting
Stringifier<Money> moneyStringifier = (conv, money) -> {
if (money == null) return "null";
return money.getCurrency().getSymbol() +
money.getAmount().setScale(2, RoundingMode.HALF_UP);
};

// User object with privacy handling
Stringifier<User> userStringifier = (conv, user) -> {
if (user == null) return "null";
// Mask sensitive information
String email = user.getEmail();
String maskedEmail = email.replaceAll("(.{3}).*(@.*)", "$1***$2");
return user.getName() + " <" + maskedEmail + ">";
};

// Registration
var converter = BasicBeanConverter.builder()
.defaultSettings()
.addStringifier(Money.class, moneyStringifier)
.addStringifier(User.class, userStringifier)
.build();

// Usage in tests
assertBean(args().setBeanConverter(converter),
order, "total,customer", "$99.99,{John Smith <joh***@example.com>}");

Recursive Stringifier

// Complex nested object stringifier
Stringifier<TreeNode> treeStringifier = new Stringifier<TreeNode>() {
@Override
public String stringify(BeanConverter conv, TreeNode node) {
if (node == null) return "null";
StringBuilder sb = new StringBuilder();
sb.append(node.getValue());
if (!node.getChildren().isEmpty()) {
sb.append("[");
for (int i = 0; i < node.getChildren().size(); i++) {
if (i > 0) sb.append(",");
sb.append(stringify(conv, node.getChildren().get(i)));
}
sb.append("]");
}
return sb.toString();
}
};

// Creates output like: "root[child1[grandchild1,grandchild2],child2]"

Using Custom Converters

// Create converter with custom formatting
var converter = BasicBeanConverter.builder()
.defaultSettings()
.addStringifier(LocalDate.class, date ->
date.format(DateTimeFormatter.ISO_LOCAL_DATE))
.addStringifier(Money.class, money ->
money.getAmount().toPlainString())
.build();

// Use in assertions
assertBean(args().setBeanConverter(converter),
order, "date,total", "2023-12-01,99.99");

Best Practices

When to Create Custom Stringifiers

  • Domain objects with complex string representations
  • Types requiring specific formatting (dates, currencies, etc.)
  • Privacy-sensitive objects that need masking
  • Legacy objects without proper toString() implementations

Stringifier Guidelines

  • Keep stringifiers simple and focused on string conversion
  • Handle null values explicitly
  • Avoid side effects or state modifications
  • Consider thread safety for shared stringifiers
  • Use clear, consistent formatting that aids test readability

Performance Considerations

  • Stringifiers are called frequently during testing
  • Avoid expensive operations (database calls, file I/O)
  • Cache computed values when appropriate
  • Consider using lazy evaluation for complex conversions

See Also

Discussion

Share feedback or follow-up questions for this page directly through GitHub.