2012-04-25 115 views
6

所以我们假设我想比较两个DOMDocument对象。他们有相同的内容,但顺序和格式可能会关闭。例如,第一个输出该XML:如何比较类似的XML与PHPUnit?

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

正如你所看到的,它们包含相同的XML结构,但一些元素可能在不同的顺序和格式是完全随机的。

如果我做的:

$this->assertEquals(); 

测试当然会失败。我不想仅测试XML结构,而是测试内容。

任何想法?

+0

如何删除所有的空格,然后比较那里sha1哈希? – 2012-04-25 17:26:19

回答

3
+1

虽然这可能在理论上回答这个问题,[这将是更可取的](http://meta.stackexchange.com/q/8259)在这里包括答案的基本部分,并提供参考链接。 – Nanne 2014-02-26 11:45:33

+1

事实上,现在这个链接被破坏了,使得这个答案在今天没有用处。 – nIcO 2014-10-27 16:57:06

+0

@nIcO修复了损坏的链接 – 2014-10-28 15:56:07

3

哪个版本的PHPUnit是这样的?我很确定最近的版本都支持DomDocument比较。

简短版本:使用$doc->preserveWhiteSpace设置删除空格,然后使用$doc->C14N()去掉注释并获取可以比较的字符串。


OK,这里有一个脚本,你可以玩,请注意EOD;线不能有任何尾随或前导空格。

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

//下一块是同一个文件的一部分,我只是做这个格式断,使StackOverflow的语法高亮系统不呛。

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

输出与$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> 

输出与$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> 

注意$doc->C14N()可能比较慢,但是我认为它很可能是剥离出来的意见是可取的。请注意,所有这些还假定XML中的空白不重要,并且可能存在一些使用情况,其中该假设是不正确的...

1

我建议您将XML转换为DOMDocuments然后使用那些与assertEquals。它已经被PHPUnit支持 - 但是这可能无法满足您的所有需求。

可以重新格式的文件,并重新加载它们为好,看到PHP XML how to output nice format

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

另一个想法是进行排序,然后孩子们通过自己的标记名 - 不知道如果之前已经完成。

0

您可以使用PHPUnit的assertXmlFileEqualsXmlFile(),assertXmlStringEqualsXmlFile()和assertXmlStringEqualsXmlString()函数;然而,他们不给上有什么不同的信息,他们只让测试失败,

Failed asserting that two DOM documents are equal. 

所以,你可能需要使用PHP的XMLDiff PECL extension,或者自己写的递归比较函数。如果时间很重要,我建议不要使用DOM,而是使用SimpleXML,因为API更简单。

0

我一直在玩这里提出的一些概念,并认为我不妨发布我的最终结果。我希望能够做的事情之一是比较两个节点或两个文档的结果。 (从技术上讲,这个可以比较一个或者只要一个类似文件的第一个孩子与另一个进行比较)

基本上,如果我发送一个DomDocument,它会使用$ clone-> loadXml($ obj - > saveXml),但是如果它是一个发送的节点,它会执行$ clone-> importNode($ obj); if的顺序变得很重要,因为DomDocument也是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)); 
}