Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

phpunit testing xml output

I have written an API which gets data from another API and converts it into XML. How do I use phpunit to test that the output is the expected XML and is valid?

Should I create a sample xml with all the nodes and then check the output against it?

like image 757
Jason Avatar asked Feb 19 '12 14:02

Jason


People also ask

What is PHPUnit XML file?

PHPUnit uses XML file for configuration. This configuration file can be added to your version control system so all developers get the same output. These settings will help you make sure your tests work the way you want.

What is the PHPUnit XML configuration file used to organize tests?

The <testsuite> Element.

What is PHPUnit testing?

PHPUnit is a unit testing framework for the PHP programming language. It is an instance of the xUnit design for unit testing systems that began with SUnit and became popular with JUnit. Even a small software development project usually takes hours of hard work.


4 Answers

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

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

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

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

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

The first thought would be to use the code you have to generate the xml and place that into the test case to test to be sure that the 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 passes, everyone is happy...however, this does not allow for the expansion of the xml. An additional issue is that you are literally testing what you are outputting in the first place, this may lead to some issues further down the line.

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

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

The solution: phpUnit has a function to call assertTag() (and assertNotTag()) which will go through the xml and can assert if a tag exists, what the content is and confirm it is setup correctly. Something like the following will not break if more items are 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 is checking for a 'person' tag, which has a child tag 'eyes'. Now if you swap around the xml output to something like this:

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

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

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

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

like image 199
ChrisK Avatar answered Oct 21 '22 11:10

ChrisK


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 those options are viable, however first approach (XML as hardcoded string variable) might get bit clumsy when XMLs are large - it's probably better to move them to separate file then.

like image 45
k.m Avatar answered Oct 21 '22 11:10

k.m


I would do this. 1) your class should use: myClass extends PHPUnit_Framework_TestCase, 2) then all tests must start with [test]Function, e.g. something 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);
   }
}
like image 45
Gillsoft AB Avatar answered Oct 21 '22 10:10

Gillsoft AB


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

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

So, to check for an eyes tag within 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 for brown eyes, do this:

    $this->assertSelectEquals(
        'person > eyes', 
        'brown',
        true,
        $person->output()
    );
like image 34
CL22 Avatar answered Oct 21 '22 11:10

CL22