2012-08-25 14 views
10

Sto usando l'API Jaxb 2.0 senza usare XSD e ho creato il modello di contenuto usando le annotazioni. Voglio scrivere un test Junit per la classe che esegue il marshalling. Il mio piano originale era di confrontare la stringa XML attesa con quella effettiva per l'asserzione (scelta più ovvia). Ma trovo che marshalling crei xml dove proprietà/ordine degli attributi non è prevedibile (in realtà non so quale sia l'ordine predefinito). Ora se questo è il caso non posso assumere una stringa xml predefinita e quindi confrontarla con quella marshallata. Un altro modo in cui stavo pensando di affermare la classe Marshaller era il seguente:Come scrivere un test unitario per JAXB 2.0 Marshalling

1-Crea modello di contenuto.

2-Marshall it.

3-Unmarshall il xml creato al passaggio 2 per ottenere il modello.

Asserzione 4-Do basata sul modello in fase 1 e fase 3 per proprietà/attributi.

Ma ancora non lo trovo soddisfacente. Quale sarebbe il modo corretto di scrivere un test Junit per il marshalling in questo scenario?

Sebbene l'applicazione effettiva che utilizza l'xml sottoposto a marshalling non dipenda dall'ordine xml proprietà/attributo, ma il test di Junit sembra essere complicato.

Grazie

risposta

6

mi sono imbattuto su sua domanda, mentre googling per la stessa cosa. Se trovato questo post, ma non ha gradito l'idea di dover "analizzare" l'XML generato in seguito. Dopo aver setacciato JAXB Javadoc, ho trovato un approccio che mi piace abbastanza. Un JAXB Marshaller offre un metodo che accetta SAX ContentHandler come argomento. È possibile prendere in giro tale ContentHandler e verificare che siano stati chiamati metodi specifici con gli argomenti previsti.

Ecco un piccolo esempio. Ho scritto un matcher personalizzato Attributes che verifica solo la presenza di determinati nomi locali degli attributi, ma non controlla i valori (ancora). Spero che è risultato utile:

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

Mi piace il modo in cui questo aspetto, ma si può fornire più di un esempio? –

0

in realtà è possibile scrivere risultato atteso da confrontare con quello che viene generato da JAXB, non dimenticate di aggiungere "\ n" alla fine del risultato atteso, che è idonea ad indurre il asserzione errore

3

Per chi preferisce un test semplice, ecco quello che ho messo insieme dal post collegato in risposta di RobertB, e le risposte 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; 
    } 

} 

3

Ho dovuto affrontare lo stesso problema dei test di marshalling XML. È possibile utilizzare la libreria XmlUnit per confrontare l'xml serializzato con etalon. XmlUnit può confrontare due xml e supporta funzionalità come ignorare lo spazio, riordinare gli elementi e altre.

Ecco buon articolo da IBM developerWorks about XmlUnit,
anche se a dire si riferisce alla versione precedente di XmlUnit, dà buona spiegazione ed esempi.

Confronta XML può apparire come segue:

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

    assert !diff.hasDifferences() 
Problemi correlati