web-dev-qa-db-ja.com

JAXB2.0マーシャリングの単体テストの書き方

XSDを使用せずにJaxb2.0 apiを使用しており、アノテーションを使用してコンテンツモデルを作成しました。マーシャリングを行うクラスのJUnitテストを作成したいと思います。私の当初の計画は、アサーションのために予想されるXML文字列を実際のXML文字列と比較することでした(最も明白な選択)。しかし、マーシャリングによって、プロパティ/属性の順序が予測できないxmlが作成されることがわかりました(実際には、デフォルトの順序はわかりません)。この場合、事前定義されたxml文字列を想定して、これをマーシャリングされたものと比較することはできません。マーシャラークラスを主張するために私が考えていたもう1つの方法は、次のとおりです。

1-コンテンツモデルを作成します。

2-マーシャルします。

3-ステップ2で作成したxmlをアンマーシャリングして、モデルを取得します。

4-プロパティ/属性について、ステップ1とステップ3のモデルに基づいてアサーションを実行します。

しかし、私はまだこれを満足のいくものとは思いません。このシナリオでマーシャリング用のJUnitテストを作成する正しい方法は何でしょうか。

マーシャリングされたxmlを使用する実際のアプリケーションは、xmlのプロパティ/属性の順序に依存しませんが、JUnitテストは注意が必要なようです。

ありがとう

13
user1525825

同じことをグーグルで検索しているときに、あなたの質問に出くわしました。これが見つかった場合 post ですが、生成されたXMLを後で「解析」する必要があるという考えは気に入らなかった。 JAXB Javadocをふるいにかけた後、私は非常に好きなアプローチを見つけました。 JAXB Marshallerは、SAX ContentHandlerを引数として取るメソッドを提供します。その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());
}
9
RobertB

より簡単なテストを好む人のために、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;
    }

}

ユニットテストjaxbアンマーシャリング

5

XMLマーシャリングテストの同じ問題に直面しました。 XmlUnit ライブラリを使用して、シリアル化されたxmlをetalonと比較できます。 XmlUnitは2つのxmlを比較でき、スペースの無視、要素の並べ替えなどの機能をサポートします。

これはIBMdeveloperWorksからの良い記事です 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()
4
Petr Vasiliev

実際には、期待される結果を記述して、jaxbによって生成されるものと比較できます。期待される結果の最後に「\ n」を追加することを忘れないでください。これにより、アサーションエラーが発生する可能性があります。

0
Richard Chen