Using var_export to unit test existing code

07 Jan 2014 in Tech

When writing unit tests, I find that the most time consuming (and the most boring) part of it all is setting up your data providers. Normally I've finished writing the code and verified that it works by hand, and am looking to add regression tests.

Fortunately, if you know that your code works, there's an easy way to generate data for your providers thanks to var_export.

Imagine you have some code that takes multiple rows and merges them into one parent row, with child objects. Something that looks a bit like this:

php
# grouped.php
function groupRows($rows){
$groupedRows = array();
foreach ($rows as $row){
$groupField = $row['id'];
if (!isset($groupedRows[$groupField])){
$groupedRows[$groupField] = array(
"id" => $row['id'],
"content" => $row['content'],
"subcontent" => array()
);
}
$groupedRows[$groupField]['subcontent'][] = $row['subcontent'];
}
return array_values($groupedRows);
}

Given the following input:

php
array(
array("id" => 1, "content" => "Foo", "subcontent" => "Bar"),
array("id" => 1, "content" => "Foo", "subcontent" => "Baz"),
array("id" => 2, "content" => "Bee", "subcontent" => "Boo")
);

We expect the following output:

php
array(
array("id" => 1, "content" => "Foo", "subcontent" => array("Bar", "Baz")),
array("id" => 2, "content" => "Bee", "subcontent" => array("Boo"))
);

Taking the input and creating the expected output by hand can become quite tiresome once you have more than a few possible inputs. Let's create a data provider that contains our inputs, and specifies null as our output.

php
# groupTest.php
require 'grouped.php';
class groupTest extends PHPUnit_Framework_TestCase {
/**
* @dataProvider groupDataProvider
*/
public function testGroup($input, $expected)
{
$actual = groupRows($input);
$this->assertEquals($expected, $actual);
}
public function groupDataProvider(){
$data = array();
$data[] = array(
array(
array("id" => 1, "content" => "Foo", "subcontent" => "Bar"),
array("id" => 1, "content" => "Foo", "subcontent" => "Baz"),
array("id" => 2, "content" => "Bee", "subcontent" => "Boo")
),
null
);
return $data;
}
}

You'll see a failure as our output is not equal to null.

1) groupTest::testGroup with data set #0 (array(array(1, 'Foo', 'Bar')), NULL) Array (...) does not match expected type "NULL".

This is where var_export comes in. Before the assert, add the following line:

php
var_export($actual); die;

Run your test again and the output of your function will be shown in the terminal. It's in a format that you can copy and paste as valid PHP, so copy it and paste it into your data provider instead of null. Comment out the var_export line and run your test again. It should pass this time. var_export adds new lines, array indices and trailing commas, so feel free to clean the output up a little if you want. Don't forget that it's still valid if you want to copy and paste it straight in though.

Congrats! You just used var_export to generate test data. Repeat this process a few more times with more example inputs to build up your test case and you're done.

Here's me working through it: