How to compare similar XML with PHPUnit?

So, let's say I want to compare two DOMDocument objects. They have the same content, but the order and formatting can be turned off. For example, the first one outputs this XML:

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

Other outputs:

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

As you can see, they contain the same XML structure, but some elements may be in a different order, and the formatting is completely random.

If I do this:

 $this->assertEquals(); 

The test, of course, will fail. I do not want to test only the XML structure, but also the content.

Any ideas?

+6
source share
5 answers

What version of PHPUnit is this? I am sure the latest versions support DomDocument comparison.

Short version: use the $doc->preserveWhiteSpace option to remove spaces, and then use $doc->C14N() to strip the comments and get a line that you can compare.


OK, here is a script that you can play with, note that the lines are EOD; cannot have any trailing or leading spaces.

  $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; 

// The next block is part of the same file, I just make this formatting gap so that the StackOverflow syntax highlighting system does not suffocate.

 $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"; 

Exit with $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> 

Exit with $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> 

Note that $doc->C14N() may be slower, but I think it seems likely that it is advisable to remove comments. Note that all of this also assumes that spaces in your XML are not important, and there are probably some use cases when this assumption is wrong ...

+3
source

I suggest you turn XML into DOMDocuments , and then use assertEquals with these. It is already supported by PHPUnit. However, this may not cover all your needs.

You can reformat the documents and reload them, see PHP XML for how to output a good format :

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

Another idea is to sort the children later by their tag - I don’t know if this has been done before.

+1
source

You can use the PHPUnit functions assertXmlFileEqualsXmlFile (), assertXmlStringEqualsXmlFile () and assertXmlStringEqualsXmlString (); however, they do not give information about what is different, they only allow the test to fail with

 Failed asserting that two DOM documents are equal. 

So, you can use the PHP extension XMLDiff PECL or write your own recursive comparison function. If time matters, I would recommend not using the DOM, but SimpleXML instead because of a simpler API.

0
source

I played with some of the concepts presented here and thought I could post my final result. One of the things I wanted to do was compare the results of two nodes or two documents. (technically, this can be compared either until the first child of a similar document is compared with another)

Basically, if I send to DomDocument, it clones it using $ clone-> loadXml ($ obj-> saveXml), but if it is sent to node, it does $ clone-> importNode ($ obj); The if order becomes important because DomDocument is also an instance of 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)); } 
0
source

Source: https://habr.com/ru/post/914153/


All Articles