Phpunit-тестирование xml-вывода

Я написал API, который получает данные из другого API и преобразует его в XML. Как использовать phpunit для проверки того, что вывод является ожидаемым XML и действителен?

Должен ли я создать образец xml со всеми узлами, а затем проверить вывод на него?

Ответ 1

Это стало результатом поиска. Я думал, что его можно одобрить, для тех, кто сталкивается с этой проблемой тестирования сгенерированной функции/класса фидера XML.

Есть много способов проверить выход xml правильно, некоторые из них легче других.

Недавно я завершил аналогичный script тот, о котором OP спрашивал в настоящее время.

Если у вас был следующий частичный xml-вывод из функции (позвоните ему $person- > output()):

<person>
    <eyes>blue</eyes>
    <hair>
        <style>curly</style>
    </hair>
</person>

Первой мыслью было бы использовать код, который вы должны сгенерировать xml, и поместить его в тестовый пример, чтобы убедиться, что xml не изменился, примерно так:

function testWholeOutput() {

    $person = new person();
    $person->setEyes("blue");
    $person->setHairStyle("curly");

    $this-assertEquals(file_get_contents("data\pregenerated_xml.xml"), $person->output());
}

Тест проходит, все довольны... однако это не позволяет расширять xml. Еще одна проблема заключается в том, что вы в буквальном смысле проверяете, что вы выводите, в первую очередь, это может привести к некоторым проблемам дальше по линии.

Что произойдет, если будет добавлена ​​новая функция, в которой требуется знание цвета волос? Тест сломается, и вам нужно будет сделать еще один xml-вывод из script, чтобы подтвердить, что вывод xml все еще работает правильно.

Кроме того, если тест прерывается, мы понятия не имеем, где он сломался, только что новая строка отличается от старой строки.

Решение: phpUnit имеет функцию для вызова assertTag() (и assertNotTag()), которая будет проходить через xml и может утверждать, существует ли тег, что содержимое и убедитесь, что оно настроено правильно. Что-то вроде следующего не будет нарушено, если к выходу xml добавится больше элементов:

function testOutputPersonHasEyes() {

    $person = new person();
    $person->setEyes("blue");
    $person->setHairStyle("curly");

    $this->assertTag(
        array('tag' => 'person',
        'child' => array('tag' => 'eyes')
            ), 
            $person->output());
    }

Здесь assertTag проверяет тег 'person', который имеет дочерний тег 'eyes'. Теперь, если вы поменяете файл xml на что-то вроде этого:

<person>
    <hair>
            <style>curly</style>
    </hair>
    <eyes>blue</eyes>
</person>

Вы все равно пройдете вышеуказанный тест, так как это все еще действительный xml и по-прежнему действительный вывод для любых скриптов, которые потребляют этот xml.

Очевидно, что вам нужно будет написать тесты, чтобы убедиться, что контент как ожидалось, и что другие элементы верны, но структура позволяет проводить менее ложные отрицательные тесты и меньше работать в долгосрочной перспективе, хотя и в ущерб еще немного времени, когда развивается.

Для дополнительной функциональности Тобиас Шлитт написал замечательную статью об модульном тестировании генерации xml в phpUnit, а также предоставляет другую альтернативу этому, создав dom и используя его оболочку вокруг класса testcase и тестирование с использованием php xPath, а также хорошее объяснение в отношении плюсов и минусов при этом.

Ответ 2

Обычно существуют два подхода:

  • Жесткие коды ожидали XML как строковые переменные в вашем тестовом классе и использовали эти строки для сравнения с фактическим результатом.
  • Создавать ожидаемые XML как обычные файлы файловой системы, но принадлежащие к вашей кодовой базе (вы рассматриваете их как ресурсы). В тесте вы загружаете ожидаемый файл и еще раз сравниваете его с результатом.

Оба эти параметра жизнеспособны, однако первый подход (XML как жестко заданная строковая переменная) может стать немного неуклюжим, когда XML-массивы являются большими - вероятно, лучше переместить их в отдельный файл.

Ответ 3

Я бы сделал это. 1) ваш класс должен использовать: myClass расширяет PHPUnit_Framework_TestCase, 2), то все тесты должны начинаться с функции [test], например. что-то вроде этого:

function testFunction()
{
   $testXml = '<xml><message>Hi there PHP</message></xml>';
   $xml = simplexml_load_string($testXml, 'SimpleXMLElement', LIBXML_NOCDATA);

   if ($xml !== false && isset($xml->message)) {

      //either this
      var_dump($xml->message);
      $this->assertEquals('Hi there PHP', $xml->message);

      //or this, should be stdClass
      $xmlObj = json_decode(json_encode((array) xml), 1);
      var_dump($xmlObj->message);
      $this->assertEquals('Hi there PHP', $xmlObj->message);
   }
}

Ответ 4

Это можно сделать, используя этот компонент phpunit, phpunit-dom-assertions, который может быть установлен с использованием композитора.

Затем используйте assertSelectEquals() вместо phpunit assertTag() (теперь обесценивается), чтобы проверить содержимое тега.

Итак, чтобы проверить тег eyes внутри person, а не:

$this->assertTag(
    array('tag' => 'person',
    'child' => array('tag' => 'eyes')
        ), 
        $person->output());
}

Сделайте это:

    $this->assertSelectCount(
        'person > eyes', 
        true,
        $person->output()
    );

И чтобы смотреть конкретно на коричневые глаза, сделайте следующее:

    $this->assertSelectEquals(
        'person > eyes', 
        'brown',
        true,
        $person->output()
    );