2017-02-13 4 views
0

J'ai donc l'entrée suivante, la sortie attendue et XML de sortie réelle:xmlunit: Erreur avec ElementSelectors.conditionalBuilder

input.xml

<Request> 
    <EmailSubjectLine>Main Contact &amp; No Reported To</EmailSubjectLine> 
    <ProductRq> 
     <Signon> 
     <ClientDt>1/6/2017 11:25:45 AM</ClientDt> 
     <CustLangPref>en-US</CustLangPref> 
     </Signon> 
     <SvcRq> 
      <RqUID>xxxxxxxx-2802-xxxx-xxxx-bf8361xxxxxx</RqUID> 
      <NotificationRq> 
       <TransactionRequestDt>2017-01-06</TransactionRequestDt> 
       <Currency>USD</Currency> 
      </NotificationRq> 
     </SvcRq> 
    </ProductRq> 
<!-- rest of input --> 
</Request> 

attendu-output.xml

<ProductRq xmlns="http://test.org/standards/intake"> 
    <Audit> 
     <TransID>Test</TransID> 
    </Audit> 
    <Signon> 
     <ClientDt>1/6/2017 11:25:45 AM</ClientDt> 
     <CustLangPref>en-US</CustLangPref> 
    </Signon> 
    <SvcRq> 
     <RqUID>xxxxxxxx-2802-xxxx-xxxx-bf8361xxxxxx</RqUID> 
     <NotificationRq> 
      <RqUID>Test</RqUID> 
      <TransactionRequestDt>2017-01-06</TransactionRequestDt> 
      <Currency>USD</Currency> 
     </NotificationRq> 
    </SvcRq> 
    <!-- rest of expected-output --> 
</ProductRq> 

actuelle-sortie.xml

<ProductRq xmlns="http://test.org/standards/intake"> 
    <Audit> 
     <TransID>123534Abwe-asdcv-1258qw-asd</TransID> 
    </Audit> 
    <Signon> 
     <ClientDt>1/6/2017 11:25:45 AM</ClientDt> 
     <CustLangPref>en-US</CustLangPref> 
    </Signon> 
    <SvcRq> 
     <RqUID>xxxxxxxx-2802-xxxx-xxxx-bf8361xxxxxx</RqUID> 
     <NotificationRq> 
      <RqUID>CG-17Dawe-12354-Hw35Sf</RqUID> 
      <TransactionRequestDt>2017-01-06</TransactionRequestDt> 
      <Currency>USD</Currency> 
     </NotificationRq> 
    </SvcRq> 
    <!-- rest of actual-output --> 
</ProductRq> 

Je les compare à l'ensemble Diff suivi:

MyTest.java

 Diff diff = DiffBuilder 
       .compare(xmlExpectedOutput) 
       .withTest(xmlOutput) 
       .normalizeWhitespace() 
       .withNodeMatcher(new DefaultNodeMatcher(ElementSelectors.conditionalBuilder() 
         .whenElementIsNamed("Audit") 
         .thenUse(ElementSelectors.byXPath("./TransID", ElementSelectors.byName)) 
         .whenElementIsNamed("NotificationRq") 
         .thenUse(ElementSelectors.byXPath("./RqUID", ElementSelectors.byName)) 
         .elseUse(ElementSelectors.byNameAndText) 
         .build() 
         )) 
       .checkForSimilar() 
       .build(); 

Je reçois les différences suivantes quand je lance l'entrée ci-dessus et comparer avec prévu -output.xml:

[Expected child '{http://test.org/standards/intake}RqUID' but was 'null' - comparing <RqUID...> at /ProductRq[1]/SvcRq[1]/NotificationRq[1]/RqUID[1] to <NULL> (DIFFERENT), Expected child 'null' but was '{http://test.org/standards/intake}RqUID' - comparing <NULL> to <RqUID...> at /ProductRq[1]/SvcRq[1]/NotificationRq[1]/RqUID[1] (DIFFERENT)]

Je ne comprends pas pourquoi mon sélecteur d'élément ne fonctionnerait pas, est-ce que je l'utilise incorrectement? Mon objectif est de trouver les identifiants TransmissionId ou NotificationRq/RqUID pour les faire correspondre aux versions de sortie attendues, sinon utiliser le nom et le texte pour les autres éléments car ces éléments contiennent des identifiants uniques qui changent à chaque test et ne peuvent être prédits (en vue de créer plus tard un sélecteur plus complexe, par exemple pour comparer ProductRq via le nom et l'attribut en tant qu'espace de noms). Y at-il quelque chose qui me manque, et suis-je capable de combiner les 2 sélecteurs XPath ensemble plutôt que plusieurs lignes quand/alors et le cas par défaut?

Remarque: le fichier XML est transformé via xslt. L'espace de noms sur PRoductRq n'est pas présent sur le document source; la source est copié, l'espace de noms ajouté à ProductRq puis envoyé pour la sortie ainsi que des suppressions éléments/modifications/ajouts

Répondre

1

xmlunit dit les RqUID éléments à l'intérieur du NotificationRq ne correspondraient pas et bien sûr, ils sont différents.

.whenElementIsNamed("NotificationRq") 
.thenUse(ElementSelectors.byXPath("./RqUID", ElementSelectors.byName)) 

signifie: quand xmlunit essaie de trouver un partenaire pour un élément NotificationRq alors il doit rechercher un NotificationRq qui a un enfant RqUID - et utiliser uniquement l'élément RqUID.

Il ne définit aucune règle pour aucun autre élément, en particulier RqUID lui-même. Pour RqUID éléments les règles par défaut applicables et

.elseUse(ElementSelectors.byNameAndText) 

dit: xmlunit accepte que deux éléments sous forme de paires si leurs noms et le match texte imbriqué. Ce qui n'est pas le cas pour les éléments RqUID en question.

Votre tout ElementSelector dit

  • match de Audit s s'ils ont TransID enfants de contenu arbitraire.
  • correspondre NotificationRq si elles ont RqUID de contenu arbitraire.
  • utilisation nom de l'élément et le texte imbriqué autrement

qui ne correspond pas à votre exemple. En regardant votre XML que vous vouliez probablement

  • presque tout correspondance par nom de l'élément et le texte imbriqué (bien que l'exemple le nom de l'élément serait suffisant)
  • ignorer le texte imbriqué des TransId enfants de Audit s
  • ignorer le texte imbriqué des RqUID enfants de NotificationRq

il n'y a pas prédicats pour « élément intégré appelé foo si elle est un enfant d'un élément nommé bar », il pourrait être quelque chose comme

Predicate<Element> transIdInAudit = e -> { 
    if (e == null || e.getParentNode() == null) { 
     return false; 
    } 
    return "TransID".equals(e.getLocalName()) && "Audit".equals(e.getParentNode().getLocalName()); 
}; 

que vous voulez probablement faire :-) généralisables

Avec que vous souhaitez utiliser

.withNodeMatcher(new DefaultNodeMatcher(ElementSelectors.conditionalBuilder() 
    .when(transIdInAudit) 
    .thenUse(ElementSelectors.byName) 
    .when(rqUIDInNotificationRq) // similar to transIdInAudit 
    .thenUse(ElementSelectors.byName) 
    .elseUse(ElementSelectors.byNameAndText) 
    .build()) 

Peut-être que vous voulez vraiment faire correspondre SvcRq si ils ont RqUID correspondant, peut-être pas. Si c'est le cas, vous utiliserez la structure que vous utilisez actuellement pour NotificationRq. Cela ne suffira pas en soi pour ignorer le texte imbriqué des éléments correspondants TransId et RqUID, cela garantira seulement que XMLUnit choisira les nœuds que vous voulez utiliser. Pour le texte imbriqué, vous aurez besoin d'un DifferenceEvaluator. Étant donné que vous utilisez ElementSelectors.byNameAndText par défaut, vous savez que les textes imbriqués sont les mêmes pour tous les nœuds correspondants à l'exception des deux éléments spécifiques pour lesquels vous souhaitez ignorer le contenu. Donc un DifferenceEvaluator comme

DifferenceEvaluators.chain(DifferenceEvaluators.Default, 
    DifferenceEvaluators.downgradeDifferencesToEqual(ComparisonType.TEXT_VALUE)) 

devrait fonctionner.

+0

bonne explication, je pense que j'interprétais mal le sélecteur d'élément byXPath, je pensais que c'était comme si l'élément était nommé ainsi, puis associez cet enfant avec le sélecteur fourni, pas qu'il ne doit utiliser que le RqUID avec le règles par défaut. Je suis finalement arrivé à une solution en mettant en œuvre 2 DifferenceEvaluators mais c'est beaucoup plus propre – jbailie1991