diff --git a/vpro-shared-test/README.adoc b/vpro-shared-test/README.adoc new file mode 100644 index 000000000..af3ee8c98 --- /dev/null +++ b/vpro-shared-test/README.adoc @@ -0,0 +1,24 @@ += Test related utilities +:toc: + +== Assertj + +- xml related assertions +- json related assertions +- serialization related assertions + +== Testcontainers + +(optional dependencies) + +- postgresql +- elasticsearch + +== Jupiter/Junit5 + +Some extension like + +- AbortOnException +- DoAfterException +- TestMDC +- TimingExtension diff --git a/vpro-shared-test/src/main/java/nl/vpro/test/util/jaxb/JAXBTestUtil.java b/vpro-shared-test/src/main/java/nl/vpro/test/util/jaxb/JAXBTestUtil.java index eedd65de2..e6228e7c5 100644 --- a/vpro-shared-test/src/main/java/nl/vpro/test/util/jaxb/JAXBTestUtil.java +++ b/vpro-shared-test/src/main/java/nl/vpro/test/util/jaxb/JAXBTestUtil.java @@ -4,9 +4,8 @@ */ package nl.vpro.test.util.jaxb; -import jakarta.xml.bind.*; -import jakarta.xml.bind.annotation.XmlRootElement; import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; import java.io.*; import java.lang.annotation.Annotation; @@ -14,7 +13,7 @@ import java.util.Arrays; import java.util.List; import java.util.function.Consumer; -import java.util.stream.Collectors; +import java.util.function.Supplier; import javax.xml.XMLConstants; import javax.xml.namespace.QName; @@ -26,6 +25,8 @@ import javax.xml.transform.stream.StreamResult; import javax.xml.transform.stream.StreamSource; import javax.xml.validation.*; +import jakarta.xml.bind.*; +import jakarta.xml.bind.annotation.XmlRootElement; import org.apache.commons.io.IOUtils; import org.apache.commons.io.output.StringBuilderWriter; @@ -33,6 +34,8 @@ import org.assertj.core.api.Fail; import org.w3c.dom.Element; import org.w3c.dom.*; +import org.w3c.dom.ls.DOMImplementationLS; +import org.w3c.dom.ls.LSSerializer; import org.xml.sax.*; import org.xmlunit.XMLUnitException; import org.xmlunit.builder.DiffBuilder; @@ -49,6 +52,7 @@ * @author Roelof Jan Koekoek * @since 1.6 */ +@Slf4j public class JAXBTestUtil { private static final String LOCAL_URI = "uri:local"; @@ -61,7 +65,6 @@ public static String marshal(T object) { marshal(object, (o) -> JAXB.marshal(o, writer)); return writer.toString(); } - public static Element marshalToElement(T object) { DOMResult writer = new DOMResult(); marshal(object, (o) -> JAXB.marshal(o, writer)); @@ -133,15 +136,12 @@ public static T roundTrip(T input, String contains) { return (T)JAXB.unmarshal(new StringReader(xml), input.getClass()); } - /** - * Checks whether the - * - * @since 2.7 - */ - @SuppressWarnings("unchecked") + @SneakyThrows - public static T roundTripContains(T input, boolean namespaceAware, String... contains) { + @SuppressWarnings("unchecked") + public static Result roundTripAndSimilarResult(T input, boolean namespaceAware, String... contains) { Element xml = marshalToElement(input); + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); factory.setNamespaceAware(namespaceAware); DocumentBuilder builder = factory.newDocumentBuilder(); @@ -153,7 +153,7 @@ public static T roundTripContains(T input, boolean namespaceAware, String.. } catch (SAXException | IOException se) { throw new RuntimeException(se); } - }).collect(Collectors.toList()); + }).toList(); for (Element elementToFind : elementsToFind) { NodeList elementsByTagName = xml.getElementsByTagName(elementToFind.getTagName()); boolean found = false; @@ -176,8 +176,34 @@ public static T roundTripContains(T input, boolean namespaceAware, String.. assertThat(writer.toString()).contains(contains); } } - return (T)JAXB.unmarshal(new DOMSource(xml), input.getClass()); + + + return new Result<>((T)JAXB.unmarshal(new DOMSource(xml), input.getClass()), () -> { + String str; + try { + DOMImplementationLS lsImpl = (DOMImplementationLS) xml.getOwnerDocument().getImplementation().getFeature("LS", "3.0"); + LSSerializer serializer = lsImpl.createLSSerializer(); + serializer.getDomConfig().setParameter("xml-declaration", false); + str = serializer.writeToString(xml); + } catch (Exception e){ + log.warn(e.getMessage()); + str = marshal(input); + } + return str; + }); } + + /** + * Checks whether the + * + * @since 2.7 + */ + + public static T roundTripContains(T input, boolean namespaceAware, String... contains) { + return roundTripAndSimilarResult(input, namespaceAware, contains).rounded(); + } + + /** * Checks whether marshalled version of an object contains a certain piece of xml. * @@ -187,6 +213,11 @@ public static T roundTripContains(T input, String... contains) { return roundTripContains(input, true, contains); } + public static Result roundTripContainsResult(T input, String... contains) { + return roundTripAndSimilarResult(input, true, contains); + } + + @SafeVarargs public static T roundTripAndSimilar(String input, Class inputClazz, Consumer... build) { @@ -207,8 +238,16 @@ public static T roundTripAndSimilar(InputStream input, Class in * Marshalls input and checks if it is similar to given string. * Then unmarshals it, and marshalls it another time. The result XMl should still be similar. */ - @SuppressWarnings({"DuplicatedCode", "unchecked"}) + @SuppressWarnings({"DuplicatedCode"}) public static T roundTripAndSimilar(T input, String expected) { + return roundTripAndSimilarResult(input, expected).rounded(); + } + + /** + * @since 5.4 + */ + @SuppressWarnings({"DuplicatedCode", "unchecked"}) + public static Result roundTripAndSimilarResult(T input, String expected) { String xml = marshal(input); similar(xml, expected); Class clazz = (Class) input.getClass(); @@ -216,7 +255,7 @@ public static T roundTripAndSimilar(T input, String expected) { /// make sure unmarshalling worked too, by marshalling the result again. String xmlAfter = marshal(result); similar(xmlAfter, xml); - return result; + return new Result<>(result, xml); } public static T roundTripAndValidateAndSimilar(T input, URL xsd, InputStream expected) throws IOException, SAXException { @@ -250,13 +289,16 @@ public static T roundTripAndValidateAndSimilar(T input, URL xsd, String expe } } public static T roundTripAndSimilar(T input, InputStream expected) throws IOException { - StringWriter writer = new StringWriter(); IOUtils.copy(expected, writer, UTF_8); return roundTripAndSimilar(input, writer.toString()); } - + public static Result roundTripAndSimilarResult(T input, InputStream expected) throws IOException { + StringWriter writer = new StringWriter(); + IOUtils.copy(expected, writer, UTF_8); + return roundTripAndSimilarResult(input, writer.toString()); + } public static T roundTripAndSimilarAndEquals(T input, String expected) { @@ -340,7 +382,7 @@ public static , T> XMLObjectAssert< return new XMLObjectAssert<>(o); } - public static JAXBTestUtil.XMLStringAssert assertThatXml(String o) { + public static JAXBTestUtil.XMLStringAssert assertThatXml(CharSequence o) { return new XMLStringAssert(o); } @@ -359,6 +401,8 @@ public static class XMLObjectAssert, A> extends A rounded; + String xml; + boolean roundTrip = true; @@ -375,12 +419,15 @@ protected XMLObjectAssert(A actual) { public S isSimilarTo(String expected) { if (roundTrip) { try { - rounded = roundTripAndSimilar(actual, expected); + + Result result = roundTripAndSimilarResult(actual, expected); + rounded = result.rounded(); + xml = result.xml(); } catch (Exception e) { Fail.fail(e.getMessage(), e); } } else { - String xml = marshal(actual); + xml = marshal(actual); similar(xml, expected); } return myself; @@ -393,12 +440,15 @@ public S isSimilarTo(String expected) { public S isSimilarTo(InputStream expected) { if (roundTrip) { try { - rounded = roundTripAndSimilar(actual, expected); + + Result result = roundTripAndSimilarResult(actual, expected); + rounded = result.rounded(); + xml = result.xml(); } catch (Exception e) { Fail.fail(e.getMessage(), e); } } else { - String xml = marshal(actual); + xml = marshal(actual); similar(xml, expected); } return myself; @@ -408,19 +458,20 @@ public S isSimilarTo(InputStream expected) { @SuppressWarnings({"CatchMayIgnoreException"}) public S containsSimilar(String expected) { try { - rounded = roundTripContains(actual, expected); + Result result = roundTripContainsResult(actual, expected); + rounded = result.rounded; + xml = result.xml(); } catch (Exception e) { Fail.fail(e.getMessage(), e); } return myself; - } + /** + * As {@code assertThat(}{@link #get()}{@code )} + */ public AbstractObjectAssert andRounded() { - if (rounded == null) { - throw new IllegalStateException("No similation was done already."); - } - return assertThat(rounded); + return assertThat(get()); } public S isValid (javax.xml.validation.Validator validator) throws SAXException, IOException { @@ -428,6 +479,11 @@ public S isValid (javax.xml.validation.Validator validator) throws SAXException, return myself; } + /** + * Returns the object as it is after marshalling/unmarshalling + * @return The object after a round trip + * + */ public A get() { if (rounded == null) { throw new IllegalStateException("No similation was done already."); @@ -435,6 +491,23 @@ public A get() { return rounded; } + /** + * Returns the object as it is after marshalling/unmarshalling + * @return The object after a round trip + * + */ + public String xml() { + if (xml == null) { + throw new IllegalStateException("No marshalling was done already."); + } + return xml; + } + + public Result getResult() { + return new Result(rounded, xml); + } + + } public static class XMLStringAssert extends AbstractObjectAssert { @@ -457,6 +530,22 @@ public XMLStringAssert isSimilarTo(InputStream expected) { } } + /** + * @since 5.4 + */ + public record Result(A rounded, Supplier xmlSupplier) { + + public Result(A rounded, String xml) { + this(rounded, () -> xml); + } + + public String xml() { + return xmlSupplier.get(); + } + + } + + protected static void assertNoDifferences(Diff diff, byte[] input, byte[] expected) { if (diff.hasDifferences()) { assertThat(pretty(input)).isEqualTo(pretty(expected)); diff --git a/vpro-shared-test/src/test/java/nl/vpro/test/util/jaxb/JAXBTestUtilTest.java b/vpro-shared-test/src/test/java/nl/vpro/test/util/jaxb/JAXBTestUtilTest.java index 27a0767b9..96f8ff523 100644 --- a/vpro-shared-test/src/test/java/nl/vpro/test/util/jaxb/JAXBTestUtilTest.java +++ b/vpro-shared-test/src/test/java/nl/vpro/test/util/jaxb/JAXBTestUtilTest.java @@ -84,6 +84,19 @@ public void testContainsNoNamespaceFails() { }).isInstanceOf(AssertionError.class); } + @Test + public void testContainsFluentResult() { + Result result = assertThatXml(new A()).containsSimilar(""" + + bb + cc + """).getResult(); + assertThat(result.rounded().getB().getJ()).isEqualTo(2); + assertThatXml(result.xml()).isSimilarTo(""" + aabbcc + """); + } + @Test public void testContainsFluent() { A rounded = assertThatXml(new A()).containsSimilar("""