2012-04-25 11 views
6

Załóżmy, że chcę porównać dwa obiekty DOMDocument. Mają tę samą treść, ale kolejność i formatowanie mogą być wyłączone. Na przykład, pierwszy z nich wyprowadza ten XML:Jak porównać podobne pliki XML z PHPUnit?

<responses> 
    <response id="12"> 
     <foo>bar</foo> 


<lorem>ipsum</lorem> 
      <sit>dolor</sit> 

    </response></responses> 

drugi wyjść:

<responses> 
<response id="12"> 

      <lorem>ipsum</lorem><sit>dolor</sit> 
     <foo>bar</foo> 
          </response> 
</responses> 

Jak widać, zawierają one taką samą strukturę XML, ale niektóre elementy mogą być w innej kolejności i formatowania jest całkowicie losowy.

Jeśli zrobić:

$this->assertEquals(); 

Badanie oczywiście niepowodzeniem. Nie chcę testować tylko struktury XML, ale także zawartości.

Wszelkie pomysły?

+0

Co powiesz na usunięcie wszystkich białych znaków i porównanie tam sha1 haszy? –

Odpowiedz

3
+1

Chociaż może to teoretycznie odpowiedzieć na pytanie, [byłoby lepiej] (http://meta.stackexchange.com/q/8259), aby dołączyć istotne części odpowiedzi tutaj, i podać link dla odniesienia. – Nanne

+1

Rzeczywiście, link jest teraz uszkodzony, co czyni tę odpowiedź bezużyteczną dzisiaj. – nIcO

+0

@ nIcO naprawił zerwany link –

3

Która wersja PHPUnit to jest? Jestem pewien, że najnowsze wersje obsługują porównania DomDocument.

Krótka wersja: Użyj ustawienia $doc->preserveWhiteSpace, aby usunąć białe znaki, a następnie użyj $doc->C14N(), aby usunąć komentarze i uzyskać ciąg, który możesz porównać.


OK, oto skrypt można grać, trzeba pamiętać, że EOD; linie nie mogą mieć wszelkie końcowe lub prowadzących spacji.

$x1 = <<<EOD 
<responses> 
    <response id="12"> 
     <foo>bar</foo> 

<lorem>ipsum</lorem> 
      <sit>dolor</sit> 
     <!--This is a comment --> 

    </response></responses> 
EOD; 

$x2 = <<<EOD 
<responses> 
<response id="12"> 

      <lorem>ipsum</lorem><sit>dolor</sit> 
     <foo>bar</foo> 
     <!--This is another comment --> 
          </response> 
</responses> 
EOD; 

// Kolejny blok jest częścią tego samego pliku, po prostu co ten formatowania-break, tak aby system wyróżniania składni StackOverflow nie zadławić.

$USE_C14N = true; // Try false, just to see the difference. 

$d1 = new DOMDocument(1.0); 
$d2 = new DOMDocument(1.0); 

$d1->preserveWhiteSpace = false; 
$d2->preserveWhiteSpace = false; 

$d1->formatOutput = false; // Only useful for "pretty" output with saveXML() 
$d2->formatOutput = false; // Only useful for "pretty" output with saveXML() 

$d1->loadXML($x1); // Must be done AFTER preserveWhiteSpace and formatOutput are set 
$d2->loadXML($x2); // Must be done AFTER preserveWhiteSpace and formatOutput are set 

if($USE_C14N){ 
    $s1 = $d1->C14N(true, false); 
    $s2 = $d2->C14N(true, false); 
} else { 
    $s1 = $d1->saveXML(); 
    $s2 = $d2->saveXML(); 
} 

echo $s1 . "\n"; 
echo $s2 . "\n"; 

Wyjście z $USE_C14N=true;

<responses><response id="12"><foo>bar</foo><lorem>ipsum</lorem><sit>dolor</sit></response></responses> 
<responses><response id="12"><lorem>ipsum</lorem><sit>dolor</sit><foo>bar</foo></response></responses> 

Wyjście z $USE_C14N=false;

<?xml version="1.0"?> 
<responses><response id="12"><foo>bar</foo><lorem>ipsum</lorem><sit>dolor</sit><!--This is a comment --></response></responses> 

<?xml version="1.0"?> 
<responses><response id="12"><lorem>ipsum</lorem><sit>dolor</sit><foo>bar</foo><!--This is another comment --></response></responses> 

Zauważ, że $doc->C14N() może być wolniejsze, ale myślę, wydaje się prawdopodobne, że rozebranie komentarzy jest pożądane. Zauważ, że wszystko to zakłada również, że białe spacje w twoim XML nie są ważne, i są prawdopodobnie pewne przypadki użycia, w których to założenie nie jest właściwe ...

1

Proponuję zmienić XML w DOMDocuments, a następnie użyć assertEquals z tymi. Jest już obsługiwany przez PHPUnit - jednak może nie pokryć wszystkich Twoich potrzeb.

Można ponownie formatu dokumentów i ponownie załadować je również znaleźć PHP XML how to output nice format:

$doc->preserveWhiteSpace = false; 
$doc->formatOutput = true; 

Innym pomysłem jest, aby posortować potem dzieci przez ich zmiennej - nie wiem, czy to zostało zrobione wcześniej.

0

Można użyć phpunit za assertXmlFileEqualsXmlFile(), assertXmlStringEqualsXmlFile() i assertXmlStringEqualsXmlString() funkcji; jednak nie dają one informacje na co innego, tylko niech nie test z

Failed asserting that two DOM documents are equal. 

Więc może chcesz używać PHP XMLDiff PECL extension lub napisać własną funkcję porównywania rekurencyjnej. Jeśli czas ma znaczenie, nie polecam używać DOM zamiast SimpleXML z powodu prostszego interfejsu API.

0

Bawiłem się niektórymi przedstawionymi tutaj pojęciami i uznałem, że równie dobrze mogę opublikować mój wynik końcowy. Jedną z rzeczy, które chciałem móc zrobić, było porównanie wyników dwóch węzłów lub dwóch dokumentów. (technicznie, to można porównać albo tak długo, jak pierwsze dziecko z podobnego dokumentu jest porównywane do innego)

Zasadniczo, jeśli wyślę dokument DomDocument, klonuje go za pomocą $ clone-> loadXml ($ obj -> saveXml), ale jeśli jest to węzeł wysłany, robi to $ clone-> importNode ($ obj); Kolejność znaczników if staje się ważna, ponieważ DomDocument jest również instancją DomNode.

/** 
* @param \DOMDocument|\DOMNode $n1 
* @param \DOMDocument|\DOMNode $n2 
* @return bool 
* @throws \Exception for invalid data 
*/ 
function compareNode($n1, $n2) 
{ 
    $nd1 = new \DOMDocument('1.0', "UTF-8"); 
    if ($n1 instanceof \DOMDocument) { 
     $nd1 = $n1->cloneNode(true); 
     $nd1->preserveWhiteSpace = false; 
     $nd1->formatOutput = false; 
     $nd1->loadXML($n1->saveXML()); 
    } elseif ($n1 instanceof \DOMNode) { 
     $nd1->preserveWhiteSpace = false; 
     $nd1->formatOutput = false; 
     $nd1->importNode($n1); 
    } else { 
     throw new \Exception(__METHOD__ . " node 1 is invalid"); 
    } 

    $nd2 = new \DOMDocument('1.0', "UTF-8"); 
    if ($n2 instanceof \DOMDocument) { 
     $nd2 = $n2->cloneNode(true); 
     $nd2->preserveWhiteSpace = false; 
     $nd2->formatOutput = false; 
     $nd2->loadXML($n2->saveXML()); 
    } elseif ($n1 instanceof \DOMNode) { 
     $nd2->preserveWhiteSpace = false; 
     $nd2->formatOutput = false; 
     $nd2->importNode($n2); 
    } else { 
     throw new \Exception(__METHOD__ . " node 2 is invalid"); 
    } 

    return ($nd1->C14N(true, false) == $nd2->C14N(true, false)); 
}