How to write a Unit Test for JAXB 2.0 Marshalling
Asked Answered
F

4

14

I am using Jaxb 2.0 api without using XSD, and have created the content model using annotations. I want to write a Junit test for the class which does the marshalling . My original plan was to compare the expected XML String with the actual one for assertion(most obvious choice). But I find that marshalling creates xml where properties/attribute order is not predictable(actually I do not know what is the default order). Now if this is the case I cannot assume a predefined xml String and then compare this with the marshalled one. Another way I was thinking for asserting marshaller class was as follows:

1-Create content Model.

2-Marshall it.

3-Unmarshall the xml created at step 2 to get the model.

4-Do assertion based on model at step 1 and step 3 for properties/attributes.

But I still do not find this satisfactory. What would be the correct way to write a Junit test for marshalling in this scenario?.

Although the actual application which uses the marshalled xml is not dependent on the xml properties/attribute order, but Junit test seems to be tricky.

Thanks

Fusiform answered 25/8, 2012 at 20:3 Comment(0)
C
11

I stumbled upon your question while googling for the same thing. If found this post, but didn't like the idea of having to "parse" the generated XML afterwards. After sieving through the JAXB Javadoc, I found an approach the I quite like. A JAXB Marshaller offers a method that takes a SAX ContentHandler as an argument. You can mock that ContentHandler and verify that specific methods have been called with the expected arguments.

Here's a little example. I wrote a custom Attributes matcher that only verifies presence of certain attribute local names, but does not look at the values (yet). I hope you find this helpful:

@Mock
private ContentHandler handler;

private JAXBContext context;
private ObjectFactory factory;
private Marshaller marshaller;

@Before
public void setUp() throws Exception
{
    context = JAXBContext.newInstance(getClass().getPackage().getName());
    factory = new ObjectFactory();
    marshaller = context.createMarshaller();
}

@Test
public void test() throws Exception
{
    final UpdateDescription description = new UpdateDescription("identifier", "version");
    final JAXBElement<UpdateDescription> element = factory.createUpdateDescription(description);

    marshaller.marshal(element, handler);

    verify(handler).startDocument();
    verify(handler).startElement(anyString(), eq("description"), anyString(), any(Attributes.class));
    verify(handler).startElement(anyString(), eq("identifier"), anyString(), attrs("value"));
    verify(handler).startElement(anyString(), eq("version"), anyString(), attrs("value"));
    verify(handler).endDocument();
}

private static Attributes attrs(final String... localNames)
{
    final Matcher<Attributes> matcher = new TypeSafeMatcher<Attributes>()
    {
        private Set<String> names = Sets.<String> newHashSet(localNames);

        @Override
        public void describeTo(final Description description)
        {
            // TODO Auto-generated method stub
        }

        @Override
        public boolean matchesSafely(final Attributes item)
        {
            final Set<String> presentLocalNames = Sets.newHashSetWithExpectedSize(item.getLength());
            final int length = item.getLength();
            for (int i = 0; i < length; ++i) {
                presentLocalNames.add(item.getLocalName(i));
            }

            return Sets.difference(names, presentLocalNames).isEmpty();
        }
    };
    return new ThreadSafeMockingProgress().getArgumentMatcherStorage().reportMatcher(matcher).returnFor(
            new AttributesImpl());
}
Chelate answered 18/11, 2012 at 21:24 Comment(1)
I like the way this looks, but I can you provide more of an example?Hectoliter
F
6

For those who prefer a simpler test, here's what I put together from the post linked in RobertB's answer, and the answers here:

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.namespace.QName;
import javax.xml.transform.stream.StreamSource;

public class JaxbTestHelper {

    @SuppressWarnings({ "rawtypes", "unchecked" })
    public static Object jaxbMarshalUnmarshal(Object schemaObject) throws Exception {
        JAXBContext context = JAXBContext.newInstance(schemaObject.getClass());
        Marshaller marshaller = context.createMarshaller();
        Unmarshaller unmarshaller = context.createUnmarshaller();
        ByteArrayOutputStream output = new ByteArrayOutputStream();
        Object unmarshalledObject = null;

        try {
            marshaller.marshal(schemaObject, output);
            ByteArrayInputStream input = new ByteArrayInputStream(output.toByteArray());
            unmarshalledObject = unmarshaller.unmarshal(input);
        } catch (JAXBException e) {
            // object class not annotated with @XmlRootElement, so we have to "wrap" and "unwrap" the object
            marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
            marshaller.marshal(new JAXBElement(new QName("uri", "local"), schemaObject.getClass(), schemaObject),
                    output);

            StreamSource source = new StreamSource(new ByteArrayInputStream(output.toByteArray()));
            unmarshalledObject = unmarshaller.unmarshal(source, schemaObject.getClass()).getValue();
        }

        // callers should verify this returned object equals the schema object passed in
        // ie, mySchemaObject.equals(jaxbMarshalUnmarshal(mySchemaObject))
        return unmarshalledObject;
    }

}

Ferrol answered 18/7, 2013 at 18:40 Comment(0)
V
4

I faced with the same problem of XML marshalling testing. You can use XmlUnit library for compare you serialized xml with etalon. XmlUnit can compare two xml and supports features like ignore space, elements reordering and some other.

Here is good article from IBM developerWorks about XmlUnit,
although is describes older version of XmlUnit, it gives good explanation and examples.

Compare xml's may look like this:

Diff diff = DiffBuilder
            .compare(expectXml)
            .withTest(marshaledXml)
            //Ignore element order
            .withNodeMatcher(new DefaultNodeMatcher(ElementSelectors.byName))
            .ignoreWhitespace()
            .ignoreComments()
            .checkForSimilar()
            .build()

    assert !diff.hasDifferences()
Valdez answered 13/6, 2016 at 0:51 Comment(1)
Is there a way to ignore differences between Headers?Sudden
M
0

actually you can write expected result to compare to what is generated by jaxb, dont forget to add "\n" in the end of your expected result which is likely to cause the assertion error

Mutineer answered 13/12, 2012 at 5:48 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.