All Downloads are FREE. Search and download functionalities are using the official Maven repository.

de.codecentric.mule.assertobjectequals.AssertObjectEqualsConnector Maven / Gradle / Ivy

package de.codecentric.mule.assertobjectequals;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.EnumSet;
import java.util.List;
import java.util.Map;

import org.codehaus.jackson.JsonProcessingException;
import org.codehaus.jackson.map.ObjectMapper;
import org.mule.api.annotations.Category;
import org.mule.api.annotations.Connector;
import org.mule.api.annotations.Processor;
import org.mule.api.annotations.display.FriendlyName;
import org.mule.api.annotations.param.Default;
import org.xmlunit.builder.DiffBuilder;
import org.xmlunit.diff.DefaultComparisonFormatter;
import org.xmlunit.diff.Diff;

@Connector(name = "assert-object-equals", friendlyName = "Assert Object Equals", description = "Compares two Java Map/List based structures)")
@Category(name = "org.mule.tooling.category.munit", description = "MUnit")
public class AssertObjectEqualsConnector {

    /**
     * Compare two objects. Drill down into {@link Map} and {@link List}, use {@link Object#equals(Object)} for all
     * other classes.
     *
     * @param expected
     *            The expected value. Automatic conversions are provided:
     *            
    *
  • InputStream is read/parsed as Json
  • *
  • byte[] is parsed as Json
  • *
  • String is parsed as Json when it starts with [ or { (after trim()
  • *
* Remember: Encoding for Json is always UTF8 * @param actual * The actual value. Automatic conversions are provided: *
    *
  • InputStream is read/parsed as Json
  • *
  • byte[] is parsed as Json
  • *
  • String is parsed as Json when it starts with [ or { (after trim()
  • *
* Remember: Encoding for Json is always UTF8 * @param containsOnlyOnMaps * The actual value entry set of maps can contain more values than the expected set. So you tests do not * fail when there are more elements than expected in the result. * * @param checkMapOrder * The order of map entries is checked. The default is to ignore order of map entries. * * @param pathOptions * Options for path patterns to control the comparison. Syntax of one List entry: Zero to n * path parts. The parts can have the following syntax: *
    *
  • ?: Wildcard one, matches one element in a path
  • *
  • *: Wildcard any, matches zero to n elements in a path
  • *
  • [#]: List wildcard, matches a list entry with any index
  • *
  • [0]: Matches a list entry with the given number. 0 or positive numbers: Count from * beginning, negative number: Cound from end (-1 is last element)
  • *
  • ['.*']: Matches a map entry where the key must match the given regular expression. If * you need a ' in the expression, just write ''. The example '.*' matches all keys.
  • *
* A space as separator. One or more of the following options (case not relevant): * * CONTAINS_ONLY_ON_MAPS: The actual value entry set of maps can contain more values than the expected * set. So you tests do not fail when there are more elements than expected in the result. * * CHECK_MAP_ORDER: The order of map entries is checked. The default is to ignore order of map entries. * * IGNORE: The actual node and its subtree is ignored completely. * * @return actual, but converted to Object when it had to be parsed. * @throws Exception * When comparison fails or on technical problems (e.g. parsing) */ @Processor(friendlyName = "Compare Objects") public Object compareObjects(@FriendlyName("Expected value") Object expected, // @Default("#[payload]") @FriendlyName("Actual value") Object actual, // @Default("false") @FriendlyName("Contains only on maps") boolean containsOnlyOnMaps, // @Default("false") @FriendlyName("Check map order") boolean checkMapOrder, // @Default("#[[]]") @FriendlyName("Path patterns+options") List pathOptions) // throws Exception { Object expectedObj = convert2Object(expected); Object actualObj = convert2Object(actual); ObjectComparator comparator = createComparator(containsOnlyOnMaps, checkMapOrder, pathOptions == null ? new ArrayList() : pathOptions); Collection diff = comparator.compare(expectedObj, actualObj); if (!diff.isEmpty()) { StringBuilder message = new StringBuilder(); for (String s : diff) { if (message.length() > 0) { message.append(System.lineSeparator()); } message.append(s); } throw new AssertionError(message); } return actualObj; } /** * Compare two XML documents. See XMLUnit Wiki} how this * works * * @param expected * The expected value, XML as String, InputStream, byte[] or DOM tree. * @param actual * The actual value, XML as String, InputStream, byte[] or DOM tree. * @param xmlCompareOption * How to compare the XML documents. * * IGNORE_COMMENTS: Will remove all comment-Tags "" from test- and control-XML before * comparing. * * IGNORE_WHITESPACE: Ignore whitespace by removing all empty text nodes and trimming the non-empty ones. * * NORMALIZE_WHITESPACE: Normalize Text-Elements by removing all empty text nodes and normalizing the * non-empty ones. * * @return actual */ @Processor(friendlyName = "Compare XML") public Object compareXml(Object expected, // @Default("#[payload]") Object actual, // @Default("NORMALIZE_WHITESPACE") @FriendlyName("XML compare option") XmlCompareOption xmlCompareOption) { DiffBuilder diffBuilder = DiffBuilder.compare(expected).withTest(actual); switch (xmlCompareOption) { case IGNORE_COMMENTS: diffBuilder = diffBuilder.ignoreComments(); break; case IGNORE_WHITESPACE: diffBuilder = diffBuilder.ignoreWhitespace(); break; case NORMALIZE_WHITESPACE: diffBuilder = diffBuilder.normalizeWhitespace(); break; default: throw new IllegalArgumentException("I forgot to implement for a new enum constant."); } Diff diff = diffBuilder.build(); if (diff.hasDifferences()) { throw new AssertionError(diff.toString(new DefaultComparisonFormatter())); } return actual; } private ObjectComparator createComparator(boolean containsOnlyOnMaps, boolean checkMapOrder, List pathOptionsStrings) { PathPatternParser ppp = new PathPatternParser(); Collection patterns = new ArrayList<>(); for (String pathOptionString : pathOptionsStrings) { patterns.add(ppp.parse(pathOptionString)); } EnumSet rootOptions = EnumSet.noneOf(PathOption.class); if (containsOnlyOnMaps) { rootOptions.add(PathOption.CONTAINS_ONLY_ON_MAPS); } if (checkMapOrder) { rootOptions.add(PathOption.CHECK_MAP_ORDER); } ObjectCompareOptionsFactory optionFactory = new PatternBasedOptionsFactory(rootOptions, patterns); return new ObjectComparator(optionFactory); } private Object convert2Object(Object value) throws JsonProcessingException, IOException { if (value == null) { return null; } else if (value instanceof InputStream) { return new ObjectMapper().reader(Object.class).readValue((InputStream) value); } else if (value instanceof byte[]) { return new ObjectMapper().reader(Object.class).readValue((byte[]) value); } else if (value instanceof CharSequence) { String trimmed = ((CharSequence) value).toString().trim(); if (trimmed.startsWith("[") || trimmed.startsWith("{")) { return new ObjectMapper().reader(Object.class).readValue(trimmed); } else { return value; } } else { return value; } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy