Mahoney / slf4j-test

Implementation of SLF4J which allows easy access to logging events in tests
39 stars 24 forks source link

Enhancment - Hamcrest matchers #3

Open philipa opened 10 years ago

philipa commented 10 years ago

I'm finding the following useful:

public static Matcher<LoggingEvent> debugContaining(String substring) {
    return formattedMessageMatches(Level.DEBUG, substring);
}

/// etc.

private static Matcher<LoggingEvent> formattedMessageMatches(
    final Level debug, final String substring) {

    return new BaseMatcher<LoggingEvent>() {

        @Override
        public boolean matches(Object item) {

            if (item instanceof LoggingEvent) {
                final LoggingEvent le = (LoggingEvent)item;

                return le.getLevel().equals(debug) &&
                    getFormattedMessage(le).indexOf(substring) >= 0;
            }

            return false;
        }

        @Override
        public void describeTo(Description description) {
            description
                .appendText("LoggingEvent with level ")
                .appendValue(debug)
                .appendText(" and a formatted message containing '")
                .appendText(substring)
                .appendText("'");
        }
    };
}

private static String getFormattedMessage(LoggingEvent event) {
    return MessageFormatter.arrayFormat(
        event.getMessage(), event.getArguments().toArray()).getMessage();
}

So I can say

    assertThat(logger.getAllLoggingEvents(),
        contains(
            debugContaining("first message"),
            debugContaining("the next message")));

Perhaps there should be a LoggingEventMatchers?

quidryan commented 8 years ago

The first thing I did was create my own matcher like this.

e-g-hategan commented 5 years ago

I've found this Hamcrest matcher useful (kotlin):

import org.hamcrest.Description
import org.hamcrest.Matcher
import org.hamcrest.TypeSafeMatcher
import org.slf4j.Marker
import uk.org.lidalia.slf4jext.Level
import uk.org.lidalia.slf4jtest.LoggingEvent

data class LoggingEventMatcher(
        private val level: Matcher<Level>? = null,
        private val mdc: Matcher<Map<String, String>>? = null,
        private val marker: Matcher<Marker>? = null,
        private val throwable: Matcher<Throwable>? = null,
        private val message: Matcher<String>? = null,
        private val arguments: Matcher<List<Any>>? = null
) : TypeSafeMatcher<LoggingEvent>() {

    override fun describeTo(description: Description?) {
        description?.appendText("a LoggingEvent")
        description?.appendText("[")

        appendDescriptionForMatchers(description, linkedMapOf(
                "level" to level,
                "mdc" to mdc,
                "marker" to marker,
                "throwable" to throwable,
                "message" to message,
                "arguments" to arguments
        ))

        description?.appendText("]")
    }

    private fun appendDescriptionForMatchers(description: Description?, matchers: Map<String, Matcher<*>?>) {
        var addSeparator = false
        for ((name, matcher) in matchers) {
            if (matcher != null) {
                if (addSeparator) {
                    description?.appendText(",")
                }
                description?.appendText(name)!!.appendText("=").appendDescriptionOf(matcher)
                addSeparator = true
            }
        }
    }

    override fun matchesSafely(item: LoggingEvent): Boolean {
        return (level == null || level.matches(item.level))
                && (mdc == null || mdc.matches(item.mdc))
                && (marker == null || marker.matches(item.marker.orNull()))
                && (throwable == null || throwable.matches(item.throwable.orNull()))
                && (message == null || message.matches(item.message))
                && (arguments == null || arguments.matches(item.arguments))
    }
}

then I can assert any part of the logging event like below (kotlin again)

    assertThat(logger.loggingEvents, hasItem(LoggingEventMatcher(level = equalTo(Level.ERROR), throwable = instanceOf(RuntimeException::class.java))))