src/test/java/br/com/rooting/roxana/translator/MockedTranslator.java
public class MockedTranslator implements Translator {

    @Override
    public String translate(String key, Locale locale, List<Parameter> parameters) {
        StringBuilder keyBuilder = new StringBuilder(key);
        for (Parameter parameter : parameters) {
            keyBuilder.append(parameter.getFormattedValue(locale));
        }
        return keyBuilder.toString();
    }

    @Override
    public Locale getLocale() {
        return Locale.getDefault();
    }

}
src/test/java/br/com/rooting/roxana/config/RoxanaPropertiesMockBuilder.java
public class RoxanaPropertiesMockBuilder {

    private static final String TRANSLATOR_MESSAGES_BUNDLE_BASE_NAME = "translator-messages-bundle/messages";

    private String locale = PT_BR.getTag();

    private boolean suppressFailsTranslations = true;

    private String baseName = TRANSLATOR_MESSAGES_BUNDLE_BASE_NAME;

    private ResponseStrategy responseStrategy = TRANSLATED;

    private Boolean suppressOthersExceptions = Boolean.TRUE;

    public RoxanaProperties build() {
        RoxanaProperties mock = mock(RoxanaProperties.class);
        when(mock.getMessageBundleLocale()).thenReturn(this.getLocale());
        when(mock.getMessageBundleSuppressFailsTranslations()).thenReturn(this.isSuppressFailsTranslations());
        when(mock.getMessageBundleBaseName()).thenReturn(this.getBaseName());
        when(mock.getBusinessResponseStrategy()).thenReturn(this.getResponseStrategy());
        when(mock.getBusinessExceptionHandlerSuppressOthersExceptions()).thenReturn(this.getSuppressOthersExceptions());
        return mock;
    }

    private String getLocale() {
        return locale;
    }

    public RoxanaPropertiesMockBuilder withLocale(String locale) {
        this.locale = locale;
        return this;
    }

    private boolean isSuppressFailsTranslations() {
        return suppressFailsTranslations;
    }

    public RoxanaPropertiesMockBuilder withSuppressFailsTranslations(boolean suppressFailsTranslations) {
        this.suppressFailsTranslations = suppressFailsTranslations;
        return this;
    }

    private String getBaseName() {
        return baseName;
    }

    public RoxanaPropertiesMockBuilder withBaseName(String baseName) {
        this.baseName = baseName;
        return this;
    }

    private ResponseStrategy getResponseStrategy() {
        return responseStrategy;
    }

    public RoxanaPropertiesMockBuilder withResponseStrategy(ResponseStrategy responseStrategy) {
        this.responseStrategy = responseStrategy;
        return this;
    }

    private Boolean getSuppressOthersExceptions() {
        return this.suppressOthersExceptions;
    }

    public RoxanaPropertiesMockBuilder withSuppressOthersExceptions(Boolean suppressOthersExceptions) {
        this.suppressOthersExceptions = suppressOthersExceptions;
        return this;
    }

}
src/main/java/br/com/rooting/roxana/config/RoxanaProperties.java
@Component
@ConfigurationProperties(ROOT_NAME)
public final class RoxanaProperties {

    public static final String ROOT_NAME = "roxana";

    @Valid
    private Business business = new Business();

    @Valid
    private MessageBundle messageBundle = new MessageBundle();

    private Business getBusiness() {
        return this.business;
    }

    public void setBusiness(Business business) {
        this.business = business;
    }

    private MessageBundle getMessageBundle() {
        return this.messageBundle;
    }

    public void setMessageBundle(MessageBundle messageBundle) {
        this.messageBundle = messageBundle;
    }

    private ExceptionHandler getBusinessExceptionHandler() {
        return this.getBusiness().getExceptionHandler();
    }

    public Boolean getBusinessExceptionHandlerSuppressOthersExceptions() {
        return this.getBusinessExceptionHandler().getSuppressOthersExceptions();
    }

    public ResponseStrategy getBusinessResponseStrategy() {
        return this.getBusiness().getResponseStrategy();
    }

    public String getMessageBundleBaseName() {
        return this.getMessageBundle().getBaseName();
    }

    public String getMessageBundleLocale() {
        return this.getMessageBundle().getLocale();
    }

    public Boolean getMessageBundleSuppressFailsTranslations() {
        return this.getMessageBundle().getSuppressFailsTranslations();
    }

    public static class Business {

        @Valid
        @NotNull
        private final ExceptionHandler exceptionHandler = new ExceptionHandler();

        @Valid
        @NotBlank
        private ResponseStrategy responseStrategy = TRANSLATED;

        public Business() {
            super();
        }

        public ResponseStrategy getResponseStrategy() {
            return this.responseStrategy;
        }

        public void setResponseStrategy(ResponseStrategy responseStrategy) {
            this.responseStrategy = responseStrategy;
        }

        ExceptionHandler getExceptionHandler() {
            return this.exceptionHandler;
        }

        public enum ResponseStrategy {

            FULLY(MessageFully.class), TRANSLATED(MessageTranslated.class), UNCHANGED(MessageUnchanged.class);

            private final Class<?> concreteMessageClass;

            ResponseStrategy(final Class<?> concreteClass) {
                this.concreteMessageClass = concreteClass;
            }

            public Class<?> getConcreteMessageClass() {
                return this.concreteMessageClass;
            }
        }

        public static class ExceptionHandler {

            private Boolean suppressOthersExceptions = Boolean.TRUE;

            public ExceptionHandler() {
                super();
            }

            public Boolean getSuppressOthersExceptions() {
                return suppressOthersExceptions;
            }

            public void setSuppressOthersExceptions(Boolean suppressOthersExceptions) {
                this.suppressOthersExceptions = suppressOthersExceptions;
            }

        }
    }

    public static class MessageBundle {

        public static final String MESSAGE_BUNDLE_BASE_NAME_PROPERTY = "roxana.message-bundle.base-name";
        public static final String MESSAGE_BUNDLE_LOCALE_PROPERTY = "roxana.message-bundle.locale";
        private static final String DEFAULT_MESSAGE_BUNDLE_BASE_NAME = "messages";

        @NotBlank
        private String baseName = DEFAULT_MESSAGE_BUNDLE_BASE_NAME;

        private String locale;

        private Boolean suppressFailsTranslations = true;

        public MessageBundle() {
            super();
        }

        public String getBaseName() {
            return this.baseName;
        }

        public void setBaseName(String baseName) {
            this.baseName = baseName;
        }

        public String getLocale() {
            return this.locale;
        }

        public void setLocale(String locale) {
            this.locale = locale;
        }

        public Boolean getSuppressFailsTranslations() {
            return this.suppressFailsTranslations;
        }

        public void setSuppressFailsTranslations(Boolean suppressFailsTranslations) {
            this.suppressFailsTranslations = suppressFailsTranslations;
        }

    }

}
src/test/java/br/com/rooting/roxana/UnitTest.java
public abstract class UnitTest<T> {

    @SuppressWarnings("unchecked")
    protected Class<T> getUnitTestClass() {
        ParameterizedType parameterizedType = (ParameterizedType) this.getClass().getGenericSuperclass();
        Type actualArgument = parameterizedType.getActualTypeArguments()[0];

        if (ParameterizedType.class.isAssignableFrom(actualArgument.getClass())) {
            return (Class<T>) ((ParameterizedType) actualArgument).getRawType();
        }

        return (Class<T>) actualArgument;
    }

}
src/main/java/br/com/rooting/roxana/config/RoxanaProperties.java
public enum ResponseStrategy {

    FULLY(MessageFully.class), TRANSLATED(MessageTranslated.class), UNCHANGED(MessageUnchanged.class);

    private final Class<?> concreteMessageClass;

    ResponseStrategy(final Class<?> concreteClass) {
        this.concreteMessageClass = concreteClass;
    }

    public Class<?> getConcreteMessageClass() {
        return this.concreteMessageClass;
    }
}
src/main/java/br/com/rooting/roxana/exception/UnexpectedException.java
@BusinessException(responseCode = HttpStatus.INTERNAL_SERVER_ERROR)
public class UnexpectedException extends Exception {

    public static final String ROXANA_KEY = Translator.getInterpolatedKeyOf(UnexpectedException.class.getName());

    private static final long serialVersionUID = 1L;

}
src/test/java/br/com/rooting/roxana/utils/ReflectionUtils.java
public static boolean isPackagePrivate(int modifier) {
    return !Modifier.isPrivate(modifier) &&
            !Modifier.isProtected(modifier) &&
            !Modifier.isPublic(modifier);
}
src/main/java/br/com/rooting/roxana/response/Response.java
public class Response {

    private final List<Message> messages = new ArrayList<>();

    Response(final List<Message> messages) {
        if (messages == null) {
            throw new IllegalArgumentException();
        }

        this.messages.addAll(messages);
    }

    public List<Message> getMessages() {
        return Collections.unmodifiableList(messages);
    }

}
src/main/java/br/com/rooting/roxana/translator/Translator.java
static String getInterpolatedKeyOf(String key) {
    return INTERPOLATION_PREFIX + key + INTERPOLATION_SUFFIX;
}
src/main/java/br/com/rooting/roxana/translator/Translator.java
public interface Translator {

    String INTERPOLATION_PREFIX = "{";

    String INTERPOLATION_SUFFIX = "}";

    String PARAMETER_INTERPOLATION_PREFIX = "[";

    String PARAMETER_INTERPOLATION_SUFFIX = "]";

    String NOT_FOUND_DELIMITER = "???";

    static String getInterpolatedKeyOf(String key) {
        return INTERPOLATION_PREFIX + key + INTERPOLATION_SUFFIX;
    }

    String translate(final String key, final Locale locale, final List<Parameter> parameters);

    default String translate(final String key, final Locale locale, final Parameter... parameters) {
        return this.translate(key, locale, Arrays.asList(parameters));
    }

    default String translate(final String key, final List<Parameter> parameters) {
        return this.translate(key, this.getLocale(), parameters);
    }

    default String translate(final String key, final Parameter... parameters) {
        return this.translate(key, this.getLocale(), parameters);
    }

    Locale getLocale();

}
src/main/java/br/com/rooting/roxana/parameter/Parameter.java
public final class Parameter {

    private final ParameterType type;

    private final String name;

    private final Object value;

    private Parameter(final ParameterType type, final String name, final Object value) {
        if (type == null || name == null || value == null) {
            throw new IllegalArgumentException();
        }
        this.type = type;
        this.name = name;
        this.value = value;
    }

    public static Parameter create(final String name, final Object value) {
        return new Parameter(STRING, name, value);
    }

    public static Parameter createDateParameter(final String name,
                                                final Object o,
                                                final DateStyle style,
                                                final Boolean considerTime,
                                                final String pattern)
            throws UnsupportedParameterConversionException {

        if (o instanceof Date) {
            return Parameter.createDateParameter(name, (Date) o, style, considerTime, pattern);
        } else if (o instanceof Calendar) {
            return Parameter.createDateParameter(name, (Calendar) o, style, considerTime, pattern);
        } else if (o instanceof LocalDate) {
            return Parameter.createDateParameter(name, (LocalDate) o, style, considerTime, pattern);
        } else if (o instanceof LocalDateTime) {
            return Parameter.createDateParameter(name, (LocalDateTime) o, style, considerTime, pattern);
        }
        throw new UnsupportedParameterConversionException(DATE, o);
    }

    public static Parameter createDateParameter(final String name,
                                                final Date date,
                                                final DateStyle style,
                                                final Boolean considerTime,
                                                final String pattern) {

        return new Parameter(DATE, name, DateTimeParameterObject.create(date, style, considerTime, pattern));
    }

    public static Parameter createDateParameter(final String name,
                                                final Calendar calendar,
                                                final DateStyle style,
                                                final Boolean considerTime,
                                                final String pattern) {

        return new Parameter(DATE, name, DateTimeParameterObject.create(calendar, style, considerTime, pattern));
    }

    public static Parameter createDateParameter(final String name,
                                                final LocalDate localDate,
                                                final DateStyle style,
                                                final Boolean considerTime,
                                                final String pattern) {

        return new Parameter(DATE, name, DateTimeParameterObject.create(localDate, style, considerTime, pattern));
    }

    public static Parameter createDateParameter(final String name,
                                                final LocalDateTime localDateTime,
                                                final DateStyle style,
                                                final Boolean considerTime,
                                                final String pattern) {

        return new Parameter(DATE, name, DateTimeParameterObject.create(localDateTime, style, considerTime, pattern));
    }

    public static Parameter createCurrencyParameter(final String name, final Object o) throws UnsupportedParameterConversionException {
        if (o instanceof BigDecimal) {
            return Parameter.createCurrencyParameter(name, (BigDecimal) o);
        } else if (o instanceof Double) {
            return Parameter.createCurrencyParameter(name, (Double) o);
        } else if (o instanceof Float) {
            return Parameter.createCurrencyParameter(name, (Float) o);
        }
        throw new UnsupportedParameterConversionException(CURRENCY, o);
    }

    public static Parameter createCurrencyParameter(final String name, final BigDecimal bigDecimalParam) {
        Double moneyValue = Parameter.treatBigDecimal(bigDecimalParam).doubleValue();
        return new Parameter(CURRENCY, name, moneyValue);
    }

    public static Parameter createCurrencyParameter(final String name, final Double doubleParam) {
        return Parameter.createCurrencyParameter(name, BigDecimal.valueOf(doubleParam));
    }

    public static Parameter createCurrencyParameter(final String name, final Float floatParam) {
        return Parameter.createCurrencyParameter(name, BigDecimal.valueOf(floatParam));
    }

    public static BigDecimal treatBigDecimal(BigDecimal bigDecimal) {
        return bigDecimal.setScale(2, BigDecimal.ROUND_HALF_EVEN);
    }

    public String getFormattedValue(Locale locale) {
        switch (this.getType()) {
            case STRING: {
                return this.getValue().toString();
            }

            case DATE: {
                return ((DateTimeParameterObject) this.getValue()).getFormatterDate(locale);
            }

            case CURRENCY: {
                return NumberFormat.getCurrencyInstance(locale).format(this.getValue());
            }

            default:
                return this.getValue().toString();
        }
    }

    public ParameterType getType() {
        return this.type;
    }

    public String getName() {
        return this.name;
    }

    public Object getValue() {
        return this.value;
    }

    @Override
    public boolean equals(Object object) {
        if (object == null) {
            return false;
        } else if (!(object instanceof Parameter)) {
            return false;
        }

        Parameter parameter = (Parameter) object;
        return this.getType().equals(parameter.getType())
                && this.getName().equals(parameter.getName())
                && this.getValue().equals(parameter.getValue());
    }

    @Override
    public int hashCode() {
        return 7 * (this.getType().hashCode() + this.getName().hashCode());
    }

}


Question: import br.com.rooting.roxana.UnitTest;
import br.com.rooting.roxana.config.RoxanaProperties;
import br.com.rooting.roxana.config.RoxanaProperties.Business.ResponseStrategy;
import br.com.rooting.roxana.config.RoxanaPropertiesMockBuilder;
import br.com.rooting.roxana.exception.UnexpectedException;
import br.com.rooting.roxana.exception.mapper.BusinessException;
import br.com.rooting.roxana.exception.mapper.MultiBusinessException;
import br.com.rooting.roxana.message.*;
import br.com.rooting.roxana.parameter.Parameter;
import br.com.rooting.roxana.response.Response;
import br.com.rooting.roxana.translator.MockedTranslator;
import br.com.rooting.roxana.translator.Translator;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.function.Executable;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import javax.validation.*;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotBlank;
import java.lang.reflect.Constructor;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import java.util.stream.Stream;
import static br.com.rooting.roxana.translator.Translator.getInterpolatedKeyOf;
import static br.com.rooting.roxana.utils.ReflectionUtils.isPackagePrivate;
import static java.lang.reflect.Modifier.isPublic;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.mock;
    void processMultiBusinessExceptionTest() throws Exception {
        String parameterValue_01 = "test01";
        String parameterValue_02 = "test02";

        MockedMultiBusinessException multi = new MockedMultiBusinessException(new MockedBusinessException(parameterValue_01),
                new MockedBusinessException(parameterValue_02));
        ResponseProcessorManager manager = this.getResponseProcessorManagerForTest();
        ResponseEntity<Response> response = manager.getProcessedResponse(multi);
        List<Message> messages = response.getBody().getMessages();

        assertEquals(2, messages.size());

        MessageFully message_01 = (MessageFully) messages.get(0);
        String translation_01 = this.getTranslator().translate(MockedBusinessException.ROXANA_KEY, message_01.getParameters());
        assertEquals(translation_01, message_01.getTranslation());
        assertEquals(1, message_01.getParameters().size());

        Parameter parameter_01_01 = message_01.getParameters().get(0);
        assertEquals(MockedBusinessException.PARAMETER_01_NAME, parameter_01_01.getName());
        assertEquals(parameterValue_01, parameter_01_01.getFormattedValue(Locale.CANADA));

        MessageFully message_02 = (MessageFully) messages.get(1);
        String translation_02 = this.getTranslator().translate(MockedBusinessException.ROXANA_KEY, message_02.getParameters());
        assertEquals(translation_02, message_02.getTranslation());
        assertEquals(1, message_02.getParameters().size());

        Parameter parameter_02_01 = message_02.getParameters().get(0);
        assertEquals(MockedBusinessException.PARAMETER_01_NAME, parameter_02_01.getName());
        assertEquals(parameterValue_02, parameter_02_01.getFormattedValue(Locale.CANADA));
    }

    @Test
    void processConstraintValidationExceptionTest() throws Exception {
        Set<ConstraintViolation<ConstraintValidatorTest>> violations = VALIDATOR.validate(new ConstraintValidatorTest());
        ConstraintViolationException constraintException = new ConstraintViolationException(violations);

        ResponseProcessorManager manager = this.getResponseProcessorManagerForTest();
        ResponseEntity<Response> response = manager.getProcessedResponse(constraintException);
        List<Message> messages = response.getBody().getMessages();

        assertEquals(2, messages.size());
        this.validateMinConstraintViolationMessage((MessageFully) messages.get(0));
        this.validateNotBlankConstraintViolationMessage((MessageFully) messages.get(1));
    }

    private void validateMinConstraintViolationMessage(final MessageFully message) {
        String translation = this.getTranslator().translate(ConstraintValidatorTest.MIN_KEY, message.getParameters());
        assertEquals(translation, message.getTranslation());
        assertEquals(MessageSeverity.ERROR, message.getSeverity());

        assertEquals(3, message.getParameters().size());

        Parameter parameter_01 = message.getParameters().get(0);
        assertEquals(ConstraintValidatorTest.PROPERTY_NAME_PARAMETER, parameter_01.getName());
        assertEquals("Positive Number", parameter_01.getValue());

        Parameter parameter_02 = message.getParameters().get(1);
        assertEquals(ConstraintValidatorTest.INVALID_VALUE_PARAMETER, parameter_02.getName());
        assertEquals(-1, parameter_02.getValue());

        Parameter parameter_03 = message.getParameters().get(2);
        assertEquals("value", parameter_03.getName());
        assertEquals(1L, parameter_03.getValue());
    }

    private void validateNotBlankConstraintViolationMessage(final MessageFully message) {
        String translation = this.getTranslator().translate(ConstraintValidatorTest.NOT_BLANK_KEY, message.getParameters());
        assertEquals(translation, message.getTranslation());
        assertEquals(MessageSeverity.ERROR, message.getSeverity());

        assertEquals(2, message.getParameters().size());

        Parameter parameter_01 = message.getParameters().get(0);
        assertEquals(ConstraintValidatorTest.PROPERTY_NAME_PARAMETER, parameter_01.getName());
        assertEquals("Not Blank", parameter_01.getValue());

        Parameter parameter_02 = message.getParameters().get(1);
        assertEquals(ConstraintValidatorTest.INVALID_VALUE_PARAMETER, parameter_02.getName());
        assertEquals("", parameter_02.getValue());
    }

    @Test
    void processWrappedGenericExceptionTest() throws Exception {
        String parameterValue = "test";
        RuntimeException runTimeException = new RuntimeException(new MockedBusinessException(parameterValue));
        ResponseProcessorManager manager = this.getResponseProcessorManagerForTest();
        ResponseEntity<Response> response = manager.getProcessedResponse(runTimeException);

        List<Message> messages = response.getBody().getMessages();

        assertEquals(1, messages.size());

        MessageFully message = (MessageFully) messages.get(0);
        assertEquals(this.getTranslator().translate(MockedBusinessException.ROXANA_KEY, message.getParameters()), message.getTranslation());
        assertEquals(1, message.getParameters().size());

        Parameter parameter_01 = message.getParameters().get(0);
        assertEquals(MockedBusinessException.PARAMETER_01_NAME, parameter_01.getName());
        assertEquals(parameterValue, parameter_01.getValue());
    }

    // TODO test if the not business exception was logged correctly.
    @Test
    void processUnexpectedExceptionTest() throws Exception {
        ResponseProcessorManager manager = this.getResponseProcessorManagerForTest();
        ResponseEntity<Response> response = manager.getProcessedResponse(new NullPointerException());
        List<Message> messages = response.getBody().getMessages();

        assertEquals(1, messages.size());

        MessageFully message = (MessageFully) messages.get(0);
        assertEquals(UnexpectedException.ROXANA_KEY, message.getTranslation());
        assertEquals(0, message.getParameters().size());
    }

    private Translator getTranslator() {
        return new MockedTranslator();
    }

    private RoxanaProperties getRoxanaProperties() {
Answer:         RoxanaPropertiesMockBuilder builder = new RoxanaPropertiesMockBuilder();