Introduktion til XMLUnit 2.x
1. Oversigt
XMLUnit 2.x er et kraftfuldt bibliotek, der hjælper os med at teste og verificere XML-indhold og er særligt praktisk, når vi ved præcis, hvad den XML skal indeholde.
Og så bruger vi primært XMLUnit inde i enhedstest for at kontrollere, at det, vi har, er gyldigt XML, at den indeholder visse oplysninger eller er i overensstemmelse med et bestemt typedokument.
Derudover med XMLUnit, vi har kontrol over, hvilken forskel der er vigtig for os og hvilken del af stilreferencen der skal sammenlignes med hvilken del af din sammenligning XML.
Da vi fokuserer på XMLUnit 2.x og ikke XMLUnit 1.x, henviser vi strengt til 2.x, når vi bruger ordet XMLUnit.
Endelig bruger vi også Hamcrest-matchere til påstande, så det er en god ide at børste op på Hamcrest, hvis du ikke er bekendt med det.
2. XMLUnit Maven-opsætning
For at bruge biblioteket i vores maven-projekter skal vi have følgende afhængigheder i pom.xml:
org.xmlunit xmlunit-core 2.2.1
Den seneste version af xmlunit-kerne kan findes ved at følge dette link. Og:
org.xmlunit xmlunit-matchere 2.2.1
Den seneste version af xmlunit-matchere findes på dette link.
3. Sammenligning af XML
3.1. Eksempler på enkle forskelle
Lad os antage, at vi har to stykker XML. De anses for at være identiske, når indholdet og sekvensen af noderne i dokumenterne er nøjagtigt de samme, så følgende test består:
@Test offentligt ugyldigt given2XMLS_whenIdentical_thenCorrect () {String controlXml = "3falsk"; Streng testXml = "3 falsk"; assertThat (testXml, CompareMatcher.isIdenticalTo (controlXml)); }
Denne næste test mislykkes, da de to stykker XML er ens, men ikke identiske med deres noder forekommer i en anden rækkefølge:
@Test offentlig ugyldighed givet2XMLSWithSimilarNodesButDifferentSequence_whenNotIdentical_thenCorrect () {String controlXml = "3 falsk"; Streng testXml = "false3"; assertThat (testXml, assertThat (testXml, not (isIdenticalTo (controlXml)));}
3.2. Eksempel på detaljeret forskel
Forskelle mellem to XML-dokumenter ovenfor registreres af Forskel motor.
Som standard og af effektivitetsårsager stopper det sammenligningsprocessen, så snart den første forskel er fundet.
For at få alle forskellene mellem to stykker XML bruger vi en forekomst af Diff klasse som sådan:
@Test offentlig ugyldighed given2XMLS_whenGeneratesDifferences_thenCorrect () {String controlXml = "3 falsk"; Streng testXml = "false3"; Diff myDiff = DiffBuilder.compare (controlXml) .withTest (testXml) .build (); Iterator iter = myDiff.getDifferences (). Iterator (); int størrelse = 0; mens (iter.hasNext ()) {iter.next (). toString (); størrelse ++; } assertThat (størrelse, størreThan (1)); }
Hvis vi udskriver de værdier, der returneres i mens loop, resultatet er som nedenfor:
Forventet element-tagnavn 'int' men var 'boolsk' - sammenlignet ved / struct [1] / int [1] med at / struct [1] / boolsk [1] (DIFFERENT) Forventet tekstværdi '3' men var 'falsk '- sammenligning af 3 at / struct [1] / int [1] / text () [1] med false at / struct [1] / boolean [1] / text () [1] (DIFFERENT) Forventet element-tagnavn' boolsk 'men var' int '- sammenligning ved / struct [1] / boolsk [1] til at / struct [1] / int [1] (DIFFERENT) Forventet tekstværdi' falsk 'men var' 3 '- sammenligning af falsk ved / struct [1] / boolsk [1] / tekst () [1] til 3 ved / struct [1] / int [1] / tekst () [1] (DIFFERENT)
Hver forekomst beskriver både typen af forskel, der findes mellem en kontrolknude og testknude og detaljerne i disse knudepunkter (inklusive XPath-placeringen af hver knude).
Hvis vi vil tvinge forskellen til stop efter den første forskel er fundet og ikke fortsætte med at tælle yderligere forskelle - vi skal levere en ComparisonController:
@Test offentligt ugyldigt givet2XMLS_whenGeneratesOneDifference_thenCorrect () {String myControlXML = "3 falsk"; Streng myTestXML = "false3"; Diff myDiff = DiffBuilder .compare (myControlXML) .withTest (myTestXML) .withComparisonController (ComparisonControllers.StopWhenDifferent) .build (); Iterator iter = myDiff.getDifferences (). Iterator (); int størrelse = 0; mens (iter.hasNext ()) {iter.next (). toString (); størrelse ++; } assertThat (størrelse, lig med (1)); }
Forskellen er lettere:
Forventet element-tagnavn 'int' men var 'boolsk' - sammenlignet ved / struct [1] / int [1] med at / struct [1] / boolsk [1] (DIFFERENT)
4. Inputkilder
Med XMLUnit, Vi kan vælge XML-data fra en række kilder, der kan være passende for vores applikations behov. I dette tilfælde bruger vi Indgang klasse med sit udvalg af statiske metoder.
For at vælge input fra en XML-fil placeret i projektets rod gør vi følgende:
@Test offentlig ugyldighed givenFileSource_whenAbleToInput_thenCorrect () {ClassLoader classLoader = getClass (). GetClassLoader (); Streng testPath = classLoader.getResource ("test.xml"). GetPath (); Streng controlPath = classLoader.getResource ("control.xml"). GetPath (); assertThat (Input.fromFile (testPath), er Lignende til (Input.fromFile (controlPath))); }
Sådan vælges en inputkilde fra en XML-streng som sådan:
@Test offentlig ugyldighed givenStringSource_whenAbleToInput_thenCorrect () {String controlXml = "3 falsk"; Streng testXml = "3 falsk"; assertThat (Input.fromString (testXml), er Lignende til (Input.fromString (controlXml))); }
Lad os nu bruge en stream som input:
@Test offentlig ugyldighed givenStreamAsSource_whenAbleToInput_thenCorrect () {assertThat (Input.fromStream (XMLUnitTests.class .getResourceAsStream ("/ test.xml")), isSimilarTo (Input.fromStream (XMLUnitTests.class.). ); }
Vi kunne også bruge Input.from (Object) hvor vi videregiver en hvilken som helst gyldig kilde, der skal løses af XMLUnit.
For eksempel kan vi sende en fil i:
@Test offentlig ugyldighed givenFileSourceAsObject_whenAbleToInput_thenCorrect () {ClassLoader classLoader = getClass (). GetClassLoader (); assertThat (Input.from (ny fil (classLoader.getResource ("test.xml"). getFile ())), erSimilarTo (Input.from (ny fil (classLoader.getResource ("control.xml"). getFile ()) ))); }
Eller en Snor:
@Test offentlig ugyldighed givenStringSourceAsObject_whenAbleToInput_thenCorrect () {assertThat (Input.from ("3false"), erSimilarTo (Input.from ("3false"))); }
Eller en Strøm:
@Test offentligt ugyldigt givenStreamAsObject_whenAbleToInput_thenCorrect () {assertThat (Input.from (XMLUnitTest.class.getResourceAsStream ("/ test.xml")), isSimilarTo (Input.from (XMLUnitTest.classStream) " ); }
og de vil alle blive løst.
5. Sammenligning af specifikke noder
I afsnit 2 ovenfor kiggede vi kun på identisk XML, fordi lignende XML har brug for en smule tilpasning ved hjælp af funktioner fra xmlunit-core bibliotek:
@Test offentligt ugyldigt given2XMLS_whenSimilar_thenCorrect () {String controlXml = "3falsk"; Streng testXml = "false3"; assertThat (testXml, isSimilarTo (controlXml)); }
Ovenstående test skal bestå, da XML'erne har lignende noder, men den mislykkes. Dette er fordi XMLUnit sammenligner kontrol- og testnoder i samme dybde i forhold til rodnoden.
Så en isSimilarTo tilstand er lidt mere interessant at teste end en isIdenticalTo tilstand. Noden 3 i controlXml vil blive sammenlignet med falsk i testXml, der automatisk giver fejlmeddelelse:
java.lang.AssertionError: Forventet: Forventet elementtagsnavn 'int' men var 'boolsk' - sammenligner at / struct [1] / int [1] med at / struct [1] / boolean [1]: 3 men: resultat var: falsk
Det er her, StandardNodeMatcher og ElementSelector klasser af XMLUnit er nyttige
Det StandardNodeMatcher klasse høres af XMLUnit i sammenligningsfasen, da den løber over noder på controlXml, for at bestemme hvilken XML-node der kommer fra testXml at sammenligne med den aktuelle XML-node, den møder i controlXml.
Inden da, StandardNodeMatcher vil allerede have hørt ElementSelector at beslutte, hvordan man matcher noder.
Vores test mislykkedes, fordi XMLUnit i standardtilstand bruger en dybdeførste tilgang til at krydse XML'erne og baseret på dokumentordre for at matche noder, dermed matches med .
Lad os tilpasse vores test, så den består:
@Test offentligt ugyldigt given2XMLS_whenSimilar_thenCorrect () {String controlXml = "3falsk"; Streng testXml = "false3"; assertThat (testXml, isSimilarTo (controlXml) .withNodeMatcher (ny DefaultNodeMatcher (ElementSelectors.byName))); }
I dette tilfælde fortæller vi det StandardNodeMatcher at når XMLUnit beder om en node, der skal sammenlignes, skal du allerede have sorteret og matchet noderne efter deres elementnavne.
Det oprindelige mislykkede eksempel lignede bestået ElementSelectors.Default til StandardNodeMatcher.
Alternativt kunne vi have brugt en Diff fra xmlunit-core snarere end at bruge xmlunit-matchere:
@Test offentlig ugyldighed given2XMLs_whenSimilarWithDiff_thenCorrect () kaster undtagelse {String myControlXML = "3 falsk"; Streng myTestXML = "false3"; Diff myDiffSimilar = DiffBuilder.compare (myControlXML) .withTest (myTestXML) .withNodeMatcher (ny DefaultNodeMatcher (ElementSelectors.byName)) .checkForSimilar (). Build (); assertFalse ("XML-lignende" + myDiffSimilar.toString (), myDiffSimilar.hasDifferences ()); }
6. Brugerdefineret DifferenceEvaluator
EN DifferenceEvaluator træffer afgørelser om resultatet af en sammenligning. Dens rolle er begrænset til at bestemme sværhedsgraden af resultatet af en sammenligning.
Det er klassen, der bestemmer, om der er to XML-stykker identisk, lignende eller forskellige.
Overvej følgende XML-stykker:
og:
I standardtilstand vurderes de teknisk som forskellige, fordi deres attr attributter har forskellige værdier. Lad os se på en test:
@Test offentligt ugyldigt givet2XMLsWithDifferences_whenTestsDifferentWithoutDifferenceEvaluator_thenCorrect () {final String control = ""; endelig streng test =""; Diff myDiff = DiffBuilder.compare (control) .withTest (test) .checkForSimilar (). Build (); assertFalse (myDiff.toString (), myDiff.hasDifferences ());}
Fejlmeddelelse:
java.lang.AssertionError: Forventet attributværdi 'abc' men var 'xyz' - sammenligning ved / a [1] / b [1] / @ attr til ved / a [1] / b [1] / @ attr
Hvis vi ikke rigtig bryr os om attributten, kan vi ændre adfærd for DifferenceEvaluator at ignorere det. Vi gør dette ved at skabe vores egne:
offentlig klasse IgnoreAttributeDifferenceEvaluator implementerer DifferenceEvaluator {private String attributeName; offentlig IgnoreAttributeDifferenceEvaluator (strengattributnavn) {this.attributeName = attributnavn; } @ Override offentlig ComparisonResult evaluere (Sammenligning sammenligning, ComparisonResult resultat) {hvis (resultat == ComparisonResult.EQUAL) returnere resultat; endelig Node controlNode = sammenligning.getControlDetails (). getTarget (); if (controlNode instanceof Attr) {Attr attr = (Attr) controlNode; hvis (attr.getName (). er lig med (attributName)) {return ComparisonResult.SIMILAR; }} returnere resultatet }}
Vi omskriver derefter vores oprindelige mislykkede test og leverer vores egen DifferenceEvaluator eksempel som sådan:
@Test offentlig ugyldighed given2XMLsWithDifferences_whenTestsSimilarWithDifferenceEvaluator_thenCorrect () {final String control = ""; endelig streng test =""; Diff myDiff = DiffBuilder.compare (kontrol) .withTest (test) .withDifferenceEvaluator (ny IgnoreAttributeDifferenceEvaluator (" attr ")) .checkForSimilar (). Build (); assertFalse (myDiff.toString (), myDiff.hasD ;}
Denne gang går det.
7. Validering
XMLUnit udfører XML-validering ved hjælp af Validator klasse. Du opretter en forekomst af det ved hjælp af for sprog fabriksmetode, mens du overfører det skema, der skal bruges i validering.
Skemaet sendes ind som en URI, der fører til dets placering, XMLUnit opsummerer skemalokaliteterne, det understøtter i Sprog klasse som konstanter.
Vi opretter typisk en forekomst af Validator klasse som sådan:
Validator v = Validator.forLanguage (Languages.W3C_XML_SCHEMA_NS_URI);
Efter dette trin, hvis vi har vores egen XSD-fil, der skal valideres mod vores XML, angiver vi simpelthen dens kilde og kalder derefter Validator'S validateInstance metode med vores XML-filkilde.
Tag for eksempel vores studerende. xsd:
Og studerende.xml:
Rajiv 18 Candy 19
Lad os derefter køre en test:
@Test offentligt ugyldigt givetXml_whenValidatesAgainstXsd_thenCorrect () {Validator v = Validator.forLanguage (Languages.W3C_XML_SCHEMA_NS_URI); v.setSchemaSource (Input.fromStream (XMLUnitTests.class.getResourceAsStream ("/ students.xsd")). build ()); ValidationResult r = v.validateInstance (Input.fromStream (XMLUnitTests.class.getResourceAsStream ("/ students.xml")). Build ()); Iterator-probs = r.getProblems (). Iterator (); mens (probs.hasNext ()) {probs.next (). toString (); } assertTrue (r.isValid ()); }
Resultatet af valideringen er en forekomst af ValidationResult som indeholder et boolesk flag, der angiver, om dokumentet er valideret med succes.
Det ValidationResult indeholder også en Iterabel med ValideringProblems hvis der er en fejl. Lad os oprette en ny XML med kaldte fejl students_with_error.xml. I stedet for , vores startkoder er alle :
Rajiv 18 Candy 19
Kør derefter denne test mod den:
@ Test offentlig ugyldighed givetXmlWithErrors_whenReturnsValidationProblems_thenCorrect () {Validator v = Validator.forLanguage (Languages.W3C_XML_SCHEMA_NS_URI); v.setSchemaSource (Input.fromStream (XMLUnitTests.class.getResourceAsStream ("/ students.xsd")). build ()); ValidationResult r = v.validateInstance (Input.fromStream (XMLUnitTests.class.getResourceAsStream ("/ students_with_error.xml")). Build ()); Iteratorprober = r.getProblems (). Iterator (); int-antal = 0; mens (probs.hasNext ()) {count ++; probs.next (). toString (); } assertTrue (count> 0); }
Hvis vi skulle udskrive fejlene i mens loop, de ville se ud som:
ValidationProblem {line = 3, column = 19, type = ERROR, message = 'cvc-complex-type.2.4.a: Ugyldigt indhold blev fundet startende med elementet' studet '. En af '{student}' forventes. ' } ValidationProblem {line = 6, column = 4, type = FEJL, meddelelse = "Elementtypen" studet "skal afsluttes med det matchende slut-tag" "." } ValidationProblem {line = 6, column = 4, type = FEJL, meddelelse = "Elementtypen" studet "skal afsluttes med det matchende slut-tag" "." }
8. XPath
Når et XPath-udtryk evalueres mod et stykke XML a NodeListe oprettes der indeholder matchningen Knuder.
Overvej dette stykke XML gemt i en fil, der hedder lærere.xml:
matematik fysik politisk uddannelse engelsk
XMLUnit tilbyder en række XPath-relaterede påstandsmetoder, som vist nedenfor.
Vi kan hente alle de kaldte noder lærer og udføre påstande om dem individuelt:
@Test offentligt ugyldigt givetXPath_whenAbleToRetrieveNodes_thenCorrect () {Iterable i = ny JAXPXPathEngine () .selectNodes ("// lærer", Input.fromFile (ny fil ("lærere.xml")). Build ()); assertNotNull (i); int-antal = 0; for (Iterator it = i.iterator (); it.hasNext ();) {count ++; Node node = it.next (); assertEquals ("lærer", node.getNodeName ()); NamedNodeMap map = node.getAttribute (); assertEquals ("afdeling", map.item (0) .getNodeName ()); assertEquals ("id", map.item (1) .getNodeName ()); assertEquals ("lærer", node.getNodeName ()); } assertEquals (2, count); }
Bemærk hvordan vi validerer antallet af underordnede noder, navnet på hver node og attributterne i hver node. Mange flere muligheder er tilgængelige efter hentning af Node.
For at bekræfte, at der findes en sti, kan vi gøre følgende:
@Test offentligt ugyldigt givetXmlSource_whenAbleToValidateExistingXPath_thenCorrect () {assertThat (Input.fromFile (ny fil ("lærere.xml")), hasXPath ("// lærere")); assertThat (Input.fromFile (ny fil ("lærere.xml")), harXPath ("// lærer")); assertThat (Input.fromFile (ny fil ("lærere.xml")), harXPath ("// emne")); assertThat (Input.fromFile (ny fil ("lærere.xml")), harXPath ("// @ afdeling")); }
For at kontrollere, at en sti ikke findes, kan vi gøre dette:
@Test offentligt ugyldigt givetXmlSource_whenFailsToValidateInExistentXPath_thenCorrect () {assertThat (Input.fromFile (ny fil ("lærere.xml")), ikke (hasXPath ("// sujet"))); }
XPaths er især nyttige, når et dokument i vid udstrækning består af kendt, uforanderligt indhold med kun en lille mængde skiftende indhold oprettet af systemet.
9. Konklusion
I denne vejledning har vi introduceret de fleste af de grundlæggende funktioner i XMLUnit 2.x og hvordan man bruger dem til at validere XML-dokumenter i vores applikationer.
Den fulde implementering af alle disse eksempler og kodestykker findes i XMLUnit GitHub-projekt.