Thursday, September 11. 2008Setting up your Zend_Test test suitesNow that Zend_Test has shipped, developers are of course asking, "How do I setup my test suite?" Fortunately, after some discussion with my colleagues and a little experimenting on my one, I can answer that now. PHPUnit offers a variety of methods for setting up test suites, some trivial and some complex. The Zend Framework test suite, for instance, goes for a more complex route, adding component-level suites that require a fair amount of initial setup, but which allow us fairly fine-grained control. However, testing and test automation should be easy and the complex approach is overkill for most of our applications. Fortunately, PHPUnit offers some other methods that make doing so relatively simple. The easiest method is to use an XML configuration file. As an example, consider the following: <phpunit> <testsuite name="My Test Suite"> <directory>./</directory> </testsuite> <filter> <whitelist> <directory suffix=".php">../library/</directory> <directory suffix=".php">../application/</directory> <exclude> <directory suffix=".phtml">../application/</directory> </exclude> </whitelist> </filter> <logging> <log type="coverage-html" target="./log/report" charset="UTF-8" yui="true" highlight="true" lowUpperBound="50" highLowerBound="80"/> <log type="testdox-html" target="./log/testdox.html" /> </logging> </phpunit>
First thing to note, relative paths are relative to the configuration file.
This allows you to run your tests from anywhere in your tests tree, Second,
providing a Drop the above into "tests/phpunit.xml" in your application, and you can start writing test cases and running the suite immediately, using the following command: % phpunit --configuration phpunit.xml I like to group my test cases by type. I have controllers, models, and often library code, and need to keep the tests organized both on the filesystem as well as for running the actual tests. There are two things I do to facilitate this. First, I create directories. For instance, I have the following hierarchy in my test suite: tests/ phpunit.xml TestHelper.php controllers/ IndexControllerTest.php (contains IndexControllerTest) ErrorControllerTest.php (contains ErrorControllerTest) ... models/ PasteTest.php (contains PasteTest) DbTable/ PasteTest.php (contains DbTable_PasteTest) ... My/ Form/ Element/ SimpleTextareaTest.php "controllers/" contains my controllers, "models/" contains my models. If I were developing a modular application, I'd have something like "blog/controllers/" instead. Library code is given the same hierarchy as is found in my "library/" directory. Second, I use docblock annotations to group my tests. I add the following to my class-level docblock in my controller test cases: /** * @group Controllers */ Models get the annotation "@group Models", etc. This allows me to run individual sets of tests on demand: % phpunit --configuration phpunit.xml --group Controllers You can specify multiple @group annotations, which means you can separate tests into modules, issue report identifiers, etc; additionally, you can add the annotations to individual test methods themselves to have really fine-grained test running capabilities. Astute readers will have noticed the "TestHelper.php" file in that directory listing earlier, and will be wondering what that's all about. A test suite needs some environmental information, just like your application does. It may need a default database adapter, altered include_paths, autoloading set up, and more. Here's what my TestHelper.php looks like: <?php /* * Start output buffering */ ob_start(); /* * Set error reporting to the level to which code must comply. */ error_reporting( E_ALL | E_STRICT ); /* * Set default timezone */ date_default_timezone_set('GMT'); /* * Testing environment */ define('APPLICATION_ENV', 'testing'); /* * Determine the root, library, tests, and models directories */ $root = realpath(dirname(__FILE__) . '/../'); $library = $root . '/library'; $tests = $root . '/tests'; $models = $root . '/application/models'; $controllers = $root . '/application/controllers'; /* * Prepend the library/, tests/, and models/ directories to the * include_path. This allows the tests to run out of the box. */ $path = array( $models, $library, $tests, get_include_path() ); set_include_path(implode(PATH_SEPARATOR, $path)); /** * Register autoloader */ require_once 'Zend/Loader.php'; Zend_Loader::registerAutoload(); /** * Store application root in registry */ Zend_Registry::set('testRoot', $root); Zend_Registry::set('testBootstrap', $root . '/application/bootstrap.php'); /* * Unset global variables that are no longer needed. */ unset($root, $library, $models, $controllers, $tests, $path); The above ensures that my APPLICATION_ENV constant is set appropriately, that error reporting is appropriate for tests (i.e., I want to see all errors), and that autoloading is enabled. Additionally, I place a couple items in my registry -- the bootstrap and test root directory. In each test case file, I then do a require_once on this file. In future versions of PHPUnit, you'll be able to specify a bootstrap file in your configuration XML that gets pulled in for each test case, and you'll be able to even further automate your testing environment setup. Hopefully this will get you started with your application testing; what are you waiting for? Comments
Display comments as
(Linear | Threaded)
Unit testing is a great idea. However in the case of Zend_Framework, as for today, it's far too complicated to be put in place. I'm an experienced php developper and I've spent those last 2 days trying to make this work with no results so far (either because of phpunit bugs, either because it won't work with ZF, etc.). So, for young php developpers, unit testing is far out of reach - at least with ZF.
There's a lot of work to be done in making things simpler ; good simple tutorials are needed to democratize this technology. I'm sorry you're having issues getting testing to work. I realize that I live and breath tests at this point, so much of what I do seems simple to *me*, but may not be to others.
If you are having issues with Zend Framework components, the best places to get assistance are the mailing lists (fw-mvc and fw-general, in particular) and IRC (#zftalk on freenode). As soon as you hit a stumbling block, I suggest using those resources -- so that you can waste minutes or hours, not days. You say you need a good simple tutorial -- what in this particular tutorial could be made easier for you, so that I can improve the experience for beginners? Thanks for your reply and for the links. I'll check this out immediately.
A serie of very short tutorials would be great : short texts (ex : 1 page - Word format -, not more) with immediate results everyone can try. Ex : 1. Test a simple ZF project with one controller, in order to setup the framework. Steps : Create the new ZF project. Add 5 lines of code to the controller. Test the controller (something usefull). Where do I put the test files in my project ? Do I have to use phpunit in command line ? 2nd tutorial. Discover the basic test mecanisms 3. Discover ZF tests - very simple tests. 4. ... more concrete exemples Etc. What novice testers need are complete steps & code in order to make it fully operationnal. We souldn't even have to understand how it work in order to play with it Thanks for your concern on this matter ! Thanks for the feedback! I'll see what I can come up with. Keep an eye on my blog and the ZF site in the coming weeks.
By the way, I'd change (1) from "Add 5 lines of code... Test..." to "Write a test specifying the expected behavior... Write the code to match the expectation..." Great !
Oh by the way I found another post of a user that has pretty much the same problems I had : http://www.zfforums.com/zend-framework-general-discussions-1/installation-configuration-3/zend_test-how-set-up-1569.html In fact, ZF really misses good documentation exemples. Like we can find in php.net in the user comments. It would really be nice to have more concrete exemples in order to make it work. I posted an issue about Zend_Form (captcha validation needed once) earlier and you replied with an exemple. That's exactly what we miss in order to make ZF a great framework. Good and practical exemples. Z.
#1.1.1.1.1
Zed
on
2008-09-12 10:42
(Reply)
Thanks for the link; I'll check that out soon.
BTW, we've debated adding comments to our manual, and are looking into the legal issues (if we incorporate any comments into the source at any point, we'd need to be sure of the intellectual property origin). I think we'll be able to accomodate it in some fashion before long. As for "That's exactly what we miss in order to make ZF a great framework," I think you'll get a number of people who claim it already is. No, you're right, ZF is a great framework, I didn't mean to underappreciate it's value when I was talking about how to make it better !
Concerning the legal issues, I'm replying to you on your personnal email. Th.
#1.1.1.1.1.1.1
Zed
on
2008-09-13 06:15
(Reply)
What I'd very much like to see is a tutorial on creating unit tests for existing projects. I have a fairly complex project spanning different modules, making heavy use of the actionstack helper and such. I'm coming across a lot of problems trying to create unit test coverage, so a tutorial on creating unit tests for existing projects would be very nice
You're saying that relative paths are relative to the configuration file. That doesn't seem to be true (at least on Windows) running it from the command line. Instead it seems to be relative to HOMEPATH of the user.
Interesting to note -- I'm on *nix systems, and the behavior is as I described. I'll make a note that this isn't true on Windows.
Here's a little example of something working :
Here's my setup : My base directory structure : /usr/local/www/application/controllers /usr/local/www/tests/ So, I'm trying to test my UserController. I wrote a file UserControllerTest.php that I put in /usr/local/www/tests ; here's it's content : This is a very basic setup, I've not managed to go further for now, but it seems it's working, so I thought I'd post this for the orther guys out there discovering unit tests with ZF. Hello,
I have recently Used Zend_test . I use startMVC in my bootstrap file. The problem is when i run my tests which asserts the controllers in which i set the layout , It gives me Debug warning like this for Action Helpers : Debug Warning: /trunk/library/Zend/Loader.php line 160 - fopen(d:/Projects/UKStudy_test/trunk/application/modules/admin/views\helpers/HeadLink.php) [function.fopen]: failed to open stream: No such file or directory what could be the problem. Please Help me do this. Regards, Ramin First off, that's not an action helper, but a view helper. Second, are you seeing those errors in your logs, or somewhere else?
Zend_View, in current released versions, calls Zend_Loader::isReadable(), which in turn calls @fopen() with the third parameter, which tells it to look on the include_path. This is more efficient than manually looping over the include_path. Note that the fopen call has error suppression; this is to *prevent* such errors as you're seeing from being displayed. However, they will still be reported in your *logs*. Hence my questions. BTW, with 1.7.0, any class using the PluginLoader, which includes Zend_View, will no longer use isReadable(), but instead rewrite the include_path briefly. From benchmarks I've done, this has around a 12.5% performance boost on real-world applications. 1.7.0 is right around the corner... Dear Matthew,
These warnings come in zend Studio Console Panel in tests written for the actions where i setLayout . as i have used headTitle , headScript, HeadLink, and .. The exact console output in zend studio is : Debug Warning: /trunk/library/Zend/Loader.php line 160 - fopen(d:/Projects/UKStudy_test/trunk/application/modules/admin/views\helpers/HeadLink.php) [function.fopen]: failed to open stream: No such file or directory Debug Warning: /trunk/library/Zend/Loader.php line 160 - fopen(d:/Projects/UKStudy_test/trunk/application/modules/admin/views\helpers/HeadStyle.php) [function.fopen]: failed to open stream: No such file or directory Debug Warning: /trunk/library/Zend/Loader.php line 160 - fopen(d:/Projects/UKStudy_test/trunk/application/modules/admin/views\helpers/Partial.php) [function.fopen]: failed to open stream: No such file or directory Debug Warning: /trunk/library/Zend/Loader.php line 160 - fopen(d:/Projects/UKStudy_test/trunk/application/modules/admin/views\helpers/Doctype.php) [function.fopen]: failed to open stream: No such file or directory Debug Warning: /trunk/library/Zend/Loader.php line 160 - fopen(d:/Projects/UKStudy_test/trunk/application/modules/admin/views\helpers/Layout.php) [function.fopen]: failed to open stream: No such file or directory which annoys me. When i resetMVCInstanse those warnings get away But the action with the Layout set in it wont assert This is my Bootstrap file : * Setting default time zone */ date_default_timezone_set('GMT'); set_include_path('.' . PATH_SEPARATOR . 'd:/Projects/UKStudy_test/trunk/library/' .PATH_SEPARATOR.'d:/Projects/UKStudy_test/trunk/Test/'. PATH_SEPARATOR . 'd:/Projects/UKStudy_test/trunk/application/modules/admin/models/' . PATH_SEPARATOR . get_include_path()); require_once('Zend/Loader.php'); Zend_Loader::registerAutoload(); $registry = Zend_Registry::getInstance(); $config = new Zend_Config_Ini('d:/Projects/UKStudy_test/trunk/application/config.ini','general'); $db = Zend_db::factory($config->db); $registry->set('config',$config); $registry->set('db',$db); Zend_Db_Table::setDefaultAdapter($db); // Setup controller //setting Module Directory and its Controllers $controller = Zend_Controller_Front::getInstance(); $controller->throwExceptions(false); // should be turned on in development time //$controller->addModuleDirectory('d:/Projects/UKStudy_test/trunk/application/modules'); // bootstrap layouts Zend_Layout::startMvc(array( 'layoutPath' => 'd:/Projects/UKStudy_test/trunk/application/layouts', 'layout' => 'Ukstudy' //TODO: fetch layout and theme from db )); //adding fireBug Writer $logger = new Zend_Log(); $writer = new Zend_Log_Writer_Firebug(); $logger->addWriter($writer); $registry->set('logger',$logger); //Starting Zend session so it May not be sent after Headers Zend_Session::start(); // run! //removing Dispatch for tests //$controller->dispatch(); Please contact me via my contact form or one of the ZF mailing lists -- this is too difficult to troubleshoot via comments.
Dear matthew which mailing list should i join so i could talk about this !?
core ?! General ?! MVC ? Documentations ?! Regards
#4.1.1.1.1
ramin
on
2008-10-12 07:33
(Reply)
Either fw-general or fw-mvc.
Hello there,
I may want to ask how Zend_test can work with Exceptions we throw in our code ? is there any type of assertion which asserts the Exception Message ?! You have a few options here. First, if you are using the ErrorHandler plugin properly, you'll get to the ErrorController's errorAction() -- so you can test to see if you got that. Second, once you have, either test for content (if you render exception messages in the view script), or pull the "error_handler" parameter from the request object, and test against it's "exception" property.
Dear Matthew ,
is there any method in zend_test or PHPunit by which you can put dependancies on tests ? for example i have a cut and paste methods which i can test individually. but as it shows, the paste is dependant to cut. i mean testing pasteAction does not satisfy if if dont test the cut. Is there any method or command by which you can say forexample : test PasteAction if testCutAction was correct ?! Regards, Ramin No, because PHPUnit itself does not yet support this.
Thanks for the tutorial.
I am not sure i totally understand how to use the TestHelper.php file. It seems like this is a replacement for your bootstrap.php file, is this correct? For example, in the Zend_Test documentation there are several methods described for bootstrapping before each *ControllerTest is run. In one instance it recommends moving much of the bootstrap setup to a plugin, and then calling the a function from "setUp()" that adds the plugin to the front controller. If we use the TestHelper.php files as described above, it seems we can ignore setting up the bootstrap in the "setUp()" function. Is that right? thanks, Elliot Actually, no. The TestHelper.php file is for bootstrapping your PHPUnit environment, not the ZF application environment. Typically, you need to setup a different include_path when testing, and may need to specify particular globals and/or settings that are test environment specific. Take a look at the ZF test suite's bootstrap to get an idea of what this file usually does.
Your ZF bootstrap is needed in addition to this, as it will setup your ZF application -- configuration, database connections, view settings, etc. Sorry Matthew but I also don't really get this TestHelper file, Im still starting my suite using phpunit --configuration phpunit.xml
Ive changed all the paths in the TestHelper but I'm getting errors telling me it cannot find the relevant libraries which tells me Autoload is failing. My dir structure is a little morer complex than your example app/ models modules/ default/ controllers/ intranet/ controllers/ Can you explain to me when the TestHelper gets called and maybe what I am doing wrong? Stick a require_once in your actual test class files pointing to the location of TestHelper.php.
Thanks Matthew, I figured this out after some much needed sleep, thanks for the reply nonetheless. All I have to do now is figure out why it's not bootstrapping
Hey,
can you fix the blog post to reflect the "require_once"? Till Thanks for the great starter article. Could you post an example of your test pages? Possibly IndexControllerTest.php ?
Actually, my pastebin demo uses Zend_Test, and its sources are on github: http:://github.com/weierophinney/pastebin
Can you give me some insight on how to debug the Invalid controller specified error? I'm getting the error as my first test runs and tries to dispatch to the default index controller.
The application works fine through the web, but my command line execution is failing with this Invalid controller specified error. Thanks for any help, I know this is obscure. Can you open this conversation up on one of the ZF mailing lists? That way you can paste the test case you're attempting to use, and perhaps also get help from others besides myself.
I'm a prospective PHP developer who learned to write tests while developing. But I only have problems testing my Zend-Framework application. It's like a curse - nothing wants to be tested
All I get are messages like "Zend_Controller_Exception: No default module defined for this application" but I'm sure I defined one... I checked it many times but I can't get rid of this message Honestly, it's hard to help without being able to see code; my suspicion is that you're not properly telling the test suite how to call your bootstrapping code.
Please post to fw-mvc mailing list with more details, and I or someone else on the list will be happy to help you out. Like Steffi, I'm having trouble with the "Zend_Controller_Exception: No default module defined for this application" message. I've looked around the various forums, and they all point towards the frontController not being set up right. I've put in various echo statements, however, and all the bits of the bootstrap and setUp are being run as I expect, but to no avail:
In bootstrap: $frontController->setControllerDirectory(ROOT_DIR.'/application/controllers', 'default'); $frontController->getDispatcher()->setDefaultModule('default'); In the test file itself: echo Zend_Controller_Front::getInstance()->getDispatcher()->getDefaultModule(); And this correctly prints out "default" as it runs. Any assistance would be very greatly appreciated! Hi, Matthew:
I understand that phpUnit is required to run Zend_Test tests, but I don't see that as a requirement or dependency anywhere in the docs. Am I missing something? Thanks! Greg Well, the API docs reference PHPUnit_Framework_TestCase, the namespace of the class references it explicitly, and the manual has phrasing such as "Testing is basically as you would expect in an PHPUnit test suite." It should be pretty clear that PHPUnit is a dependency.
I do plan to write some tutorials showing how to install PHPUnit and start building your test suites, however, and hopefully those will be incorporated into the manual or ZF site at some point. Hi Matthew,
Im not having much success with this and wondering if you could help. I went back and examined the pastebin example to get a better idea of how the tests run. I get the test suite going but it tests nothing (I have a custom testcase that simply asserts the correct controller and action) One thing that may help is if you could explain what the file scripts/loadTestDB.php is doing and why it is needed? Thank you for your explanation it really helped me out. However I have a problem I need to write:
C:\>phpunit --configuration \wamp\www\test\phpunit.xml It works but it would be nice to be able to write: C:\>phpunit --configuration phpunit.xml Do you an idea of what I need to do ? Best regards Thomas Riis If you are in the same directory as your phpunit.xml file, recent versions will automatically look for it and use it -- so all you need to do is execute "phpunit".
I see then something has to be wrong in my pear set up
After looking in the mailing list, I wrote an helper method to compile form with captchas in Zend_Test and shared it in an article:
http://ossigeno.sourceforge.net/blog/content/article/zend-test-and-captchas Do you feel that a cleaned up patch for Zend_Test (passing from Zend_Session and with custom captcha element name) will be considered? Add Comment
|
CalendarQuicksearchLinks
ArchivesCategoriesSyndicate This BlogShow tagged entries |






Matthew hat in seinem Blog einen Beitrag geschrieben, wie man Zend_Test und eine Testsuite aufsetzen kann. Der Beitrag beschreibt den Aufbau eine XML Konfigurationsdatei für PHPUnit, stellt ein Beispiel für die Verzeichnisstruktur für eine Testsuite...
Tracked: Sep 12, 01:30