-
Notifications
You must be signed in to change notification settings - Fork 27
File Export
This is a tutorial for using the FileExport PHPUnit extension.
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.
The extension can be downloaded here:
https://github.com/aiqui/phpunit-extensions/tree/master/PHPUnit/Extensions/FileExport
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");
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());
}
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).
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
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.
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.
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.
When you run phpunit normally, no export files will be saved, and it will compare against the existing saved export files.
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.
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.
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.
The export files can be used an documentation reference, providing developers with the exact output of a method.
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.