In my Beyond Frameworks talk, I explained how a component-based architecture can help answer some of the important (i.e. expensive!) questions you might face when creating long-lived apps that rely on a PHP framework. In this series of blog posts, I’m going to look at how to go about creating and working with components.
Earlier today, I created some very basic unit tests for my repustateApi component. Unit tests are an essential part of developing your component, but how do you run them?
Running Your Component’s Code From The Command-Line
Many old-skool developers choose to work largely from the command-line. You can happily run PHPUnit by hand from the command line yourself each time, but if you’re taking advantage of the extra data that PHPUnit can report back on, that soon gets to be a lot of typing! This is where the build.xml file in our skeleton comes in handy …
To run your unit tests from the command line, all you need to do is run the command phing test:
stuart:~/Devel/sental/repustateApi$ phing test Buildfile: /home/stuart/Devel/sental/repustateApi/build.xml [property] Loading /home/stuart/Devel/sental/repustateApi/build.properties > lint: [phplint] /home/stuart/Devel/sental/repustateApi/src/php/Gradwell/Repustate Api/Client.php: No syntax errors detected > run-unittests: [delete] Deleting directory /home/stuart/Devel/sental/repustateApi/review/ code-coverage [mkdir] Created dir: /home/stuart/Devel/sental/repustateApi/review/code-c overage [exec] Executing command: phpunit --bootstrap=/home/stuart/Devel/sental/ repustateApi/src/tests/unit-tests/bootstrap.php --coverage-html /home/stuart/ Devel/sental/repustateApi/review/code-coverage --coverage-clover /home/stuart /Devel/sental/repustateApi/review/logs/phpunit.xml /home/stuart/Devel/sental/ repustateApi/src/tests/unit-tests 2>&1 [exec] PHPUnit 3.5.3 by Sebastian Bergmann. [exec] [exec] .EE [exec] [exec] Time: 0 seconds, Memory: 4.00Mb [exec] [exec] There were 2 errors: [exec] [exec] 1) Gradwell\RepustateApi\ClientTest::testCanRetrieveSentimentScor eForTextByJson [exec] Exception: oh dear [exec] [exec] /home/stuart/Devel/sental/repustateApi/src/php/Gradwell/Repustate Api/Client.php:209 [exec] /home/stuart/Devel/sental/repustateApi/src/php/Gradwell/Repustate Api/Client.php:151 [exec] /home/stuart/Devel/sental/repustateApi/src/php/Gradwell/Repustate Api/Client.php:90 [exec] /home/stuart/Devel/sental/repustateApi/src/tests/unit-tests/php/G radwell/RepustateApi/ClientTest.php:70 [exec] [exec] 2) Gradwell\RepustateApi\ClientTest::testCanRetrieveSentimentScor eForUrlByJson [exec] Exception: oh dear [exec] [exec] /home/stuart/Devel/sental/repustateApi/src/php/Gradwell/Repustate Api/Client.php:209 [exec] /home/stuart/Devel/sental/repustateApi/src/php/Gradwell/Repustate Api/Client.php:151 [exec] /home/stuart/Devel/sental/repustateApi/src/php/Gradwell/Repustate Api/Client.php:116 [exec] /home/stuart/Devel/sental/repustateApi/src/tests/unit-tests/php/G radwell/RepustateApi/ClientTest.php:83 [exec] [exec] FAILURES! [exec] Tests: 3, Assertions: 1, Errors: 2. [exec] [exec] Writing code coverage data to XML file, this may take a moment. [exec] [exec] Generating code coverage report, this may take a moment. Execution of target "run-unittests" failed for the following reason: /home/st uart/Devel/sental/repustateApi/build.xml:108:40: Task exited with code 2 BUILD FAILED /home/stuart/Devel/sental/repustateApi/build.xml:108:40: Task exited with cod e 2 Total time: 0.5951 seconds
That went bang in a spectacular way! At this stage, where we have tests but don’t yet have a working Repustate API client, that’s exactly what should happen. As I work through the code, and satisfy the tests, we should quickly get to the point where we have more tests, and that they all pass.
Here’s an example of another component – our CommandLineLib – which has many tests that all succeed, just so that you can see what a successful test run should look like:
Buildfile: /home/stuart/Devel/GWC/CommandLineLib/build.xml [property] Loading /home/stuart/Devel/GWC/CommandLineLib/build.properties > lint: [phplint] /home/stuart/Devel/GWC/CommandLineLib/src/php/Phix_Project/Comman dLineLib/CommandLineParser.php: No syntax errors detected [phplint] /home/stuart/Devel/GWC/CommandLineLib/src/php/Phix_Project/Comman dLineLib/ParsedSwitches.php: No syntax errors detected [phplint] /home/stuart/Devel/GWC/CommandLineLib/src/php/Phix_Project/Comman dLineLib/DefinedArg.php: No syntax errors detected [phplint] /home/stuart/Devel/GWC/CommandLineLib/src/php/Phix_Project/Comman dLineLib/DefinedSwitch.php: No syntax errors detected [phplint] /home/stuart/Devel/GWC/CommandLineLib/src/php/Phix_Project/Comman dLineLib/DefinedSwitches.php: No syntax errors detected [phplint] /home/stuart/Devel/GWC/CommandLineLib/src/php/Phix_Project/Comman dLineLib/ParsedSwitch.php: No syntax errors detected > run-unittests: [delete] Deleting directory /home/stuart/Devel/GWC/CommandLineLib/review/c ode-coverage [mkdir] Created dir: /home/stuart/Devel/GWC/CommandLineLib/review/code-co verage [exec] Executing command: phpunit --bootstrap=/home/stuart/Devel/GWC/Com mandLineLib/src/tests/unit-tests/bootstrap.php --coverage-html /home/stuart/D evel/GWC/CommandLineLib/review/code-coverage --coverage-clover /home/stuart/D evel/GWC/CommandLineLib/review/logs/phpunit.xml /home/stuart/Devel/GWC/Comman dLineLib/src/tests/unit-tests 2>&1 [exec] PHPUnit 3.5.3 by Sebastian Bergmann. [exec] [exec] ............................................................ 60 / 78 [exec] .................. [exec] [exec] Time: 1 second, Memory: 5.75Mb [exec] [exec] OK (78 tests, 407 assertions) [exec] [exec] Writing code coverage data to XML file, this may take a moment. [exec] [exec] Generating code coverage report, this may take a moment. [echo] [echo] The code coverage report is in /home/stuart/Devel/GWC/CommandLine Lib/review/code-coverage > test: BUILD FINISHED Total time: 3.7912 seconds
Command-Line Test Run Explained
The component’s build.xml file contains targets – sequences of instructions that you can invoke by using phing. When you run the command phing test, here’s what happens:
- phing loads the build.xml file in the current working directory. So make sure you’re in your component’s top-level folder when you run phing!
- build.xml sucks in the build.properties file where we set the component’s name and version number. These properties are in a separate file so that it’s safe to replace build.xml with new versions as we improve the component’s skeleton files in the future (via the phix php-library:upgrade command).
- phing runs the ‘test’ target in build.xml, which tells it to actually run the ‘lint’ and ‘run-unittests’ targets.
- The ‘lint’ target does a syntax check on all of your PHP files. As your component grows, and the time it takes to run your unit tests grows too, a quick syntax check can save a developer wasting a surprising amount of time.
- If the syntax check is successful, the ‘run-unittests’ target runs next.
- The ‘run-unittests’ target invokes PHPUnit, telling it to use the skeleton’s bootstrap file, and to write out the results in XML format to feed into other tools later on. It also tells PHPUnit to write out code coverage data as HTML. We’ll look at that in a later blog post.
Running Your Unit Tests From Netbeans
If the command-line isn’t for you, don’t worry … the component skeleton is also designed to make it very easy to run your unit tests from inside Netbeans. I’m assuming that it will be just as easy to do this from other IDEs that support PHPUnit, but I haven’t tested any myself.
First of all, you need to setup your project in Netbeans to tell it where your tests are. Open the project’s properties dialog box:
and then set the Test Folder to be your ‘unit-tests’ folder:
Next, open the PHPUnit settings in the project’s properties dialog box, and tick the ‘Use Bootstrap’ box:
and then, tell Netbeans to use the bootstrap file inside the ‘unit-tests’ folder:
so that the PHPUnit settings look like this:
Now we’re all set to run the tests from inside Netbeans. In the project browser, simply right-click on the Client.php file, and select ‘Test’ from the popup menu. Netbeans will run the tests for you, and tell you exactly where they broke:
I hope you find these tools useful, and time-saving!
I’m going to beaver away over the weekend getting several components completed, and will be back on Monday to go through more of the things you need to know if you’re going to successfully go Beyond Frameworks and shift to a component-based architecture. Have a great weekend!