[ACCEPTED]-phpunit testing xml output-phpunit

Accepted answer
Score: 10

This came up as a search result I thought 52 it could be approved on, for anyone who 51 comes across this issue of testing a generated 50 xml feeder function/class.

There are many 49 ways to test out an xml output is correct, some 48 easier than others.

I have recently completed 47 a similar script to that which OP was asking 46 about at this time.

If you had the following 45 partial xml output from a function (let's 44 call it $person->output()):

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

The first 43 thought would be to use the code you have 42 to generate the xml and place that into 41 the test case to test to be sure that the 40 xml has not changed, something like this:

function testWholeOutput() {

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

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

Test 39 passes, everyone is happy...however, this 38 does not allow for the expansion of the 37 xml. An additional issue is that you are 36 literally testing what you are outputting 35 in the first place, this may lead to some 34 issues further down the line.

What happens 33 if a new feature is put in which requires 32 the knowledge of the hair colour? The test 31 will break and you will need to do another 30 xml output from the script in order to confirm 29 the xml output is still working correctly.

Additionally 28 if the test breaks then we have no idea 27 where it broke, just that the new string 26 is different to the old string.

The solution: phpUnit 25 has a function to call assertTag() (and assertNotTag()) which 24 will go through the xml and can assert if 23 a tag exists, what the content is and confirm 22 it is setup correctly. Something like the 21 following will not break if more items are 20 added to the xml output:

function testOutputPersonHasEyes() {

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

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

The assertTag here 19 is checking for a 'person' tag, which has 18 a child tag 'eyes'. Now if you swap around 17 the xml output to something like this:

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

You 16 will still pass the above test, since this 15 is still valid xml, and still valid output 14 for any scripts which are consuming this 13 xml.

Obviously you will need to write tests 12 to make sure the content is as expected 11 and that the other items are correct but 10 the structure allows less false negative 9 tests and less work in the long run, albeit 8 at the detriment of some more time when 7 developing.

For additional functionality, Tobias Schlitt wrote a great article on 6 unit testing xml generation in phpUnit, and 5 also provides another alternative to this 4 by creating a dom object and using it a 3 wrapper around the testcase class and testing 2 using the php xPath, as well as a nice explanation 1 on pros/cons on doing so.

Score: 5

There are usually two approaches:

  1. Hardcode expected XMLs as string variables in your test class and use those strings to compare with actual result.
  2. Create expected XMLs as regular file system files, yet belonging to your codebase (you treat them as resources). In test you load expected file, and once again compare it with result.

Both of 5 those options are viable, however first 4 approach (XML as hardcoded string variable) might 3 get bit clumsy when XMLs are large - it's 2 probably better to move them to separate 1 file then.

Score: 5

I would do this. 1) your class should use: myClass 3 extends PHPUnit_Framework_TestCase, 2) then 2 all tests must start with [test]Function, e.g. something 1 like this:

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);
   }
}
Score: 4

It can be done using this phpunit component, phpunit-dom-assertions, which can be installed 5 using composer.

Then use its assertSelectEquals() instead of 4 phpunit's assertTag() (now depreciated) to test for the contents 3 of a tag.

So, to check for an eyes tag within 2 a person, instead of:

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

Do this:

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

And to look specifically 1 for brown eyes, do this:

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

Load the XML string to a simplexml object.

    $object = simplexml_load_string($xml);

It 2 should be valid object if it can be instantiated.

    $this->assertNotFalse($object);
    $this->assertInstanceOf( \SimpleXMLElement::class, $object);

Check 1 for all XML nodes you need in your output.

    $this->assertObjectHasAttribute("vehicle", $object);
    //...

More Related questions