Customization
BCT provides flexible customization options through the @BctConfig annotation and the BctConfiguration class. These tools allow you to configure assertion behavior, customize property access, and extend BCT's capabilities to match your testing needs.
📋 Table of Contents​
- @BctConfig Annotation
- BctConfiguration Class
- Configuration Properties
- Custom Bean Converters
- Thread Safety
- Advanced Customization Topics
@BctConfig Annotation​
The @BctConfig annotation provides a declarative way to configure BCT settings for your tests. It can be applied at both the class level and method level, with method-level annotations taking precedence over class-level annotations.
Basic Usage​
import org.apache.juneau.junit.bct.annotations.BctConfig;
import org.apache.juneau.commons.lang.TriState;
@BctConfig(sortMaps=TriState.TRUE)
class MyTest {
@Test
@BctConfig(sortCollections=TriState.TRUE)
void testSomething() {
// sortMaps=true (from class), sortCollections=true (from method)
assertBean(order, "items{0{name}}", "{{Laptop}}");
}
}
Annotation Properties​
sortMaps​
Controls whether map entries are sorted by key before comparison in assertions.
@BctConfig(sortMaps=TriState.TRUE)
class MyTest {
@Test
void testMap() {
// Map entries will be sorted before comparison
Map<String, String> map = Map.of("zebra", "animal", "apple", "fruit");
assertBean(map, "apple,zebra", "fruit,animal");
}
}
Values:
TriState.TRUE- Enable map sortingTriState.FALSE- Disable map sortingTriState.UNSET- Inherit from class-level annotation (default for method-level)
sortCollections​
Controls whether collection elements are sorted before comparison in assertions.
@BctConfig(sortCollections=TriState.TRUE)
class MyTest {
@Test
void testList() {
// List elements will be sorted before comparison
List<String> list = Arrays.asList("zebra", "apple", "banana");
assertList(list, "apple", "banana", "zebra");
}
}
Values:
TriState.TRUE- Enable collection sortingTriState.FALSE- Disable collection sortingTriState.UNSET- Inherit from class-level annotation (default for method-level)
beanConverter​
Specifies a custom bean converter class to use for all assertions in the test.
public static class MyCustomConverter extends BasicBeanConverter {
public MyCustomConverter() {
super(BasicBeanConverter.builder()
.defaultSettings()
.addStringifier(LocalDate.class, date ->
date.format(DateTimeFormatter.ISO_LOCAL_DATE))
.build());
}
}
@BctConfig(beanConverter=MyCustomConverter.class)
class MyTest {
@Test
void testWithCustomConverter() {
// All assertions use MyCustomConverter
assertBean(event, "date", "2023-12-01");
}
}
Requirements:
- The converter class must have a no-arg constructor
- The constructor will be made accessible if needed (for nested classes, etc.)
- If instantiation fails, the test will fail with a descriptive error message
Class-Level vs Method-Level​
Method-level annotations override class-level annotations for the same property. Properties not specified in the method annotation inherit from the class annotation.
@BctConfig(sortMaps=TriState.TRUE, beanConverter=MyConverter.class)
class MyTest {
@Test
@BctConfig(sortMaps=TriState.FALSE, sortCollections=TriState.TRUE)
void testMethod() {
// sortMaps=false (from method, overrides class)
// sortCollections=true (from method)
// beanConverter=MyConverter (from class, inherited)
}
@Test
void testAnotherMethod() {
// sortMaps=true (from class)
// sortCollections=UNSET (default, not set)
// beanConverter=MyConverter (from class)
}
}
Using Default Converter​
To explicitly use the default converter when a class-level converter is set, use BasicBeanConverter.class:
@BctConfig(beanConverter=MyConverter.class)
class MyTest {
@Test
@BctConfig(beanConverter=BasicBeanConverter.class)
void testWithDefaultConverter() {
// Uses default converter, not MyConverter
}
}
BctConfiguration Class​
The BctConfiguration class provides a programmatic API for configuring BCT settings. It's useful when you need dynamic configuration or want to set up converters in test setup methods.
Setting Configuration Values​
import static org.apache.juneau.junit.bct.BctConfiguration.*;
@BeforeEach
void setUp() {
// Enable map sorting
set(BCT_SORT_MAPS, true);
// Enable collection sorting
set(BCT_SORT_COLLECTIONS, true);
// Set custom converter
var converter = BasicBeanConverter.builder()
.defaultSettings()
.addStringifier(LocalDate.class, date ->
date.format(DateTimeFormatter.ISO_LOCAL_DATE))
.build();
set(converter);
}
@AfterEach
void tearDown() {
// Clear all thread-local settings
clear();
}
Getting Configuration Values​
// Get a setting with a default value
boolean sortMaps = BctConfiguration.get(BCT_SORT_MAPS, false);
// Get a setting object (can check if set)
StringSetting setting = BctConfiguration.get(BCT_SORT_MAPS);
if (setting != null && setting.asBoolean()) {
// Map sorting is enabled
}
Global vs Thread-Local Settings​
Settings can be set globally (affecting all threads) or thread-locally (affecting only the current thread). Thread-local settings take precedence.
// Set global setting (affects all threads)
BctConfiguration.setGlobal(BCT_SORT_MAPS, true);
// Set thread-local setting (overrides global for this thread)
BctConfiguration.set(BCT_SORT_MAPS, false);
// Clear thread-local settings
BctConfiguration.clear();
// Clear global settings
BctConfiguration.clearGlobal();
Configuration Properties​
BCT_SORT_MAPS​
Property name: "Bct.sortMaps"
When enabled, map entries are sorted by key before comparison, ensuring consistent assertion results regardless of map implementation or insertion order.
BctConfiguration.set(BctConfiguration.BCT_SORT_MAPS, true);
BCT_SORT_COLLECTIONS​
Property name: "Bct.sortCollections"
When enabled, collection elements are sorted before comparison, ensuring consistent assertion results regardless of collection implementation or insertion order.
BctConfiguration.set(BctConfiguration.BCT_SORT_COLLECTIONS, true);
Custom Bean Converters​
Bean converters control how objects are accessed and converted to strings for comparison. BCT uses BasicBeanConverter.DEFAULT by default, but you can provide custom converters for specialized needs.
Creating a Custom Converter​
public static class MyCustomConverter extends BasicBeanConverter {
public MyCustomConverter() {
super(BasicBeanConverter.builder()
.defaultSettings()
// Add custom stringifier for LocalDate
.addStringifier(LocalDate.class, date ->
date.format(DateTimeFormatter.ISO_LOCAL_DATE))
// Add custom stringifier for Money
.addStringifier(Money.class, money ->
money.getAmount().toPlainString())
.build());
}
}
Using via Annotation​
@BctConfig(beanConverter=MyCustomConverter.class)
class MyTest {
@Test
void testWithCustomConverter() {
assertBean(order, "date,total", "2023-12-01,99.99");
}
}
Using via Programmatic API​
@BeforeEach
void setUp() {
var converter = BasicBeanConverter.builder()
.defaultSettings()
.addStringifier(LocalDate.class, date ->
date.format(DateTimeFormatter.ISO_LOCAL_DATE))
.build();
BctConfiguration.set(converter);
}
@AfterEach
void tearDown() {
BctConfiguration.clear(); // Also clears converter
}
Converter Requirements​
- Must have a no-arg constructor
- Constructor will be made accessible if needed (for nested classes, private constructors, etc.)
- If instantiation fails, a
RuntimeExceptionis thrown with a descriptive message
Thread Safety​
All configuration in BCT is thread-safe and uses thread-local storage. This ensures that:
- Parallel test execution doesn't interfere with each other's settings
- Each test thread has its own isolated configuration
- Settings are automatically cleared after each test when using
@BctConfig
Thread-Local Storage​
// Thread 1
BctConfiguration.set(BCT_SORT_MAPS, true);
// Thread 2 (running in parallel)
BctConfiguration.set(BCT_SORT_MAPS, false);
// Each thread has its own setting - no interference
Automatic Cleanup​
When using @BctConfig, settings are automatically cleared after each test method:
@BctConfig(sortMaps=TriState.TRUE)
class MyTest {
@Test
void test1() {
// sortMaps is enabled
}
@Test
void test2() {
// sortMaps is still enabled (from class annotation)
// But if test1 had method-level annotation, it would be cleared
}
}
Advanced Customization Topics​
For more advanced customization options, see:
- Stringifiers - Define how objects are converted to strings for comparison
- Listifiers - Convert collection-like objects to lists for testing
- Swappers - Transform objects before processing (unwrap Optional, Supplier, etc.)
See Also​
- juneau-bct Basics - Main BCT documentation
- Custom Error Messages - Enhanced error reporting
- Property Extractors - Custom property access logic
Share feedback or follow-up questions for this page directly through GitHub.