【问题标题】:How to compare similar XMLs with PHPUnit?如何将类似的 XML 与 PHPUnit 进行比较?
【发布时间】:2012-05-06 09:44:13
【问题描述】:

假设我想比较两个 DOMDocument 对象。它们具有相同的内容,但顺序和格式可能会关闭。例如,第一个输出这个 XML:

<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 结构,还要测试内容。

有什么想法吗?

【问题讨论】:

  • 如何删除所有空格,然后比较那里的 sha1 哈希?

标签: php zend-framework phpunit


【解决方案1】:

【讨论】:

  • 虽然理论上这可以回答这个问题,it would be preferable 在这里包含答案的基本部分,并提供链接以供参考。
  • 确实,链接现在已经断开,今天这个答案毫无用处。
  • @nIcO 修复了断开的链接
  • 示例:$this-&gt;assertXmlStringEqualsXmlString('&lt;foo&gt;&lt;bar/&gt;&lt;/foo&gt;', $xmlString);
【解决方案2】:

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

短版:使用$doc-&gt;preserveWhiteSpace设置去除空格,然后使用$doc-&gt;C14N()去除cmets,得到一个可以比较的字符串。


好的,这是一个您可以使用的脚本,请注意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-&gt;C14N() 可能会更慢,但我认为剥离 cmets 似乎是可取的。请注意,所有这些还假设您的 XML 中的空白不重要,并且可能有一些用例不正确...

【讨论】:

  • 不确定它是否已经改变,但是DOMDocument$version 参数是字符串而不是浮点数。那些使用严格模式的人会得到:DOMDocument::__construct() expects parameter 1 to be string, float given
【解决方案3】:

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

您也可以重新格式化文档并重新加载它们,请参阅PHP XML how to output nice format

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

另一个想法是按孩子的标记名对孩子进行排序 - 不知道以前是否这样做过。

【讨论】:

    【解决方案4】:

    您可以使用 PHPUnit 的 assertXmlFileEqualsXmlFile()、assertXmlStringEqualsXmlFile() 和 assertXmlStringEqualsXmlString() 函数;然而,他们没有提供不同之处的信息,他们只是让测试失败

    Failed asserting that two DOM documents are equal.
    

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

    【讨论】:

      【解决方案5】:

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

      基本上,如果我发送 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));
      }
      

      【讨论】:

        【解决方案6】:

        使用以下断言:

        $this->assertXmlStringEqualsXmlString($expected, $actual);
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2020-01-06
          • 1970-01-01
          • 2022-09-26
          • 2018-06-18
          • 1970-01-01
          • 2019-11-28
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多