13

我在不使用 XSD 的情况下使用 Jaxb 2.0 api,并使用注释创建了内容模型。我想为执行编组的类编写一个 Junit 测试。我最初的计划是将预期的 XML 字符串与断言的实际字符串进行比较(最明显的选择)。但我发现编组会创建属性/属性顺序不可预测的 xml(实际上我不知道默认顺序是什么)。现在,如果是这种情况,我不能假设一个预定义的 xml 字符串,然后将其与编组的字符串进行比较。我在考虑断言 marshaller 类的另一种方法如下:

1-创建内容模型。

2-马歇尔它。

3-解组在步骤 2 中创建的 xml 以获取模型。

4-在第 1 步和第 3 步对属性/属性进行基于模型的断言。

但我仍然不觉得这令人满意。在这种情况下编写用于编组的 Junit 测试的正确方法是什么?

虽然使用编组 xml 的实际应用程序不依赖于 xml 属性/属性顺序,但 Junit 测试似乎很棘手。

谢谢

4

4 回答 4

11

我在谷歌搜索同样的事情时偶然发现了你的问题。如果找到这篇文章,但不喜欢事后必须“解析”生成的 XML 的想法。在筛选了 JAXB Javadoc 之后,我找到了一种我非常喜欢的方法。JAXBMarshaller提供了一种将 SAXContentHandler作为参数的方法。您可以模拟它ContentHandler并验证是否已使用预期参数调用了特定方法。

这是一个小例子。我编写了一个自定义Attributes匹配器,它只验证某些属性本地名称的存在,但不查看值(还)。我希望这个对你有用:

@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());
}
于 2012-11-18T21:24:18.750 回答
6

对于那些喜欢更简单测试的人,这是我从 RobertB 的答案中链接的帖子中汇总的内容,以及此处的答案:

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

}

于 2013-07-18T18:40:00.467 回答
4

我遇到了同样的 XML 编组测试问题。您可以使用XmlUnit库将您的序列化 xml 与标准具进行比较。XmlUnit 可以比较两个 xml 并支持忽略空间、元素重新排序等功能。

这是 IBM developerWorks关于 XmlUnit的好文章,
虽然描述的是旧版本的 XmlUnit,但它提供了很好的解释和示例。

比较 xml 可能如下所示:

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

    assert !diff.hasDifferences()
于 2016-06-13T00:51:20.793 回答
0

实际上,您可以编写预期结果来与 jaxb 生成的结果进行比较,不要忘记在预期结果的末尾添加“\n”,这可能会导致断言错误

于 2012-12-13T05:48:03.110 回答