Skip to content

File Export

Charlie Hileman edited this page Nov 28, 2015 · 20 revisions

This is a tutorial for using the FileExport PHPUnit extension.

Comparing large data elements

Unit testing can be painful. Building robust tests is time consuming, particularly when looking at large data structures. If you are validating large objects or arrays, this extension may help you. Rather than trying to break them down into small individual assert statements, you can simply make the call:

$this->assertExportCompare($someBigObjectOrArray);

This will compare the var_export of the element against a previously saved export file. These export files should be saved into your repository. They can be easily updated as your project changes.

Download of file export PHPUnit extension

The extension can be downloaded here:

https://github.com/aiqui/phpunit-extensions/tree/master/PHPUnit/Extensions/FileExport

Limitation of custom options in phpunit

This extension uses custom command-line options, but currently PHPUnit will throw an exception when encountering unknown options. To resolve this problem, comment out one line of code in the file phpunit/src/Util/Getopt.php in the method parseLongOption:

161c161
<         throw new PHPUnit_Framework_Exception("unrecognized option --$opt");
---
> //        throw new PHPUnit_Framework_Exception("unrecognized option --$opt");

Initial configuration

To use the file export class, extend the abstract class:

require_once('PATH-TO-EXTENSION/FileExportCompare.php')

namespace TopName\SubName;

class ExampleTest extends \PHPUnit_FileExport_TestCase {
    public function test_getCaseOverview() {
        $this->oSomeObj = new Whatever();
        $this->assertExportCompare($this->oSomeObj->getCaseOverview());
}

How to save exported elements

There are two command-line options for saving the export files:

  • --save-exports - save the export files for one method
  • --save-all-exports - save all the export files for all methods

It's recommended that you use --save-exports so that you can carefully compare you results. Saving all the exports at one time could allow you to overlook any issues, and you may unintentionally overlook changed export files.

The options must be placed after any standard PHPUnit options. For example:

phpunit --filter test_getCaseOverview --save-exports Example.uut.php 

The files are saved in a subdirectory named phpunit_exports in the same directory where phpunit is run. A subdirectory with name of the class is created inside of phpunit_exports. If the class uses PHP namespaces, the directory includes the namespaces separated by hyphens (to avoid the inane use of backslashes).

phpunit_exports/TopName-SubName-ExampleTest/test_getCaseOverview.1

The file simply contains the contents of var_export for that given variable. The file name has an index which will increment with each call to assertExportCompare in the same method (i.e. if you compare multiple structures, the same filename will be used, but the number will increment).

When the assert fails

The export assertion takes the output of the variable via var_export, saving it to a temporary file, and comparing the output to the export file via a shell call to diff. When it fails, you would see something like this:

1) TopName\SubName\ExampleTest::test_getCaseOverview
Export file: phpunit_exports/TopName-SubName-ExampleTest/test_getCaseOverview.1
Differences found between exported file and variable
--- Exported file
+++ Actual variable

Typical work flow

1. Build your test methods

Extend your class like the example above, and build your test methods as normal. Whenever you hit a complicated structure, call assertExportCompare. Often there are dynamic elements to the structure that should be unset first, so that you are only comparing parts of the structure that will be the same with every call to phpunit.

2. Save the export files

Save your export files with either --save-exports or --save-all-exports. To be methodical, you normally would use the --filter option to select the method and --save-exports at the end to save the export files.

3. Review and manually validate the exported files

This is the most critical step. Look carefully at the output of the exported file, and review every element. If there are any problems, fix your code and save again.

4. Run phpunit without exporting files

When you run phpunit normally, no export files will be saved, and it will compare against the existing saved export files.

5. Fixing problems

If a difference is found and an exception is thrown, investigate the problem, fix your code and, if necessary, save your exported files again. Always carefully validate after saving.

Export file backups

When saving export files, if a difference is discovered between the old and new exports, the old export files are saved in a backup subdirectory that includes the date and time in the directory name. A message appears notifying the developer of the changes. If no differences are found, the backup export files are removed.

Version control

It's recommended that you save the export files under version control (e.g. git). Typically there is no need to save the backup directories; these should be removed after any changes have been investigated.

Documentation of output

The export files can be used an documentation reference, providing developers with the exact output of a method.

Limitations

This extension works very well in creating unit tests for complicated existing methods. But it may be of little value when creating tests beforehand, i.e. test-driven development.

The extension may also make unit testing too easy to build, removing the deliberate exercise of manually composing robust tests of a method's output.

Clone this wiki locally