How to write a Unit Test for JAXB 2.0 Marshalling

user1525825 picture user1525825 · Aug 25, 2012 · Viewed 24.5k times · Source

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

Answer

RobertB picture RobertB · Nov 18, 2012

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());
}