Unit testing Zend_Session - php

i have problem with testing zend_session, my test look like that:
Bootstrap.php
public function _initSession() {
try
{
Zend_Session::setSaveHandler(new APP_Session_SaveHandler_Memcached());
Zend_Session::start();
$session = new Zend_Session_Namespace('APP');
Zend_Registry::set('session', $session);
}
catch(Exception $e)
{
Zend_Registry::get('log')->err($e);
}
}
SessionTest.php
public function testSession() {
$session = Zend_Registry::get('session');
$session->setExpirationSeconds(1);
$session->testIndex = "testValue";
$this->assertEquals('testValue', $session->testIndex);
sleep(3);
$this->assertEquals(null, $session->testIndex);
}
first assertion is ok, but the second always say that value of $session->testIndex is "testValue", when it should be null right?
If i'am wrong, please tell me, because I can't figure it out.

The expiry time isn't evaluated every time you access data from the session class, just when it is first loaded. A typical PHP script should never take over a second to execute, so the case you are trying to test shouldn't ever happen in a real world situation.
I'd also add that there's not a lot of point testing functionality that exists in ZF like this - the ZF classes are already unit tested. Test your application, not the framework.

Related

Testing method with no output

I have the following method I want to test:
class SomeObject {
public function actionFromSomeController() {
$obj = new OtherObject();
$obj -> setAttributes();
$obj -> doAction();
}
}
class OtherObject {
private $_attr;
public function setAttributes() {
$this -> _attr = 'something';
Database :: execute('INSERT INTO table VALUES (' . $this -> _attr . ')');
$fileObj = new FileObj();
$content = $fileObj -> getSomeFileContent();
// do something else
}
public function doAction() {
echo $this -> _attr;
}
}
Now I want to test this method, its output depends on database content and one file on the server. It does a lot of things on the way, and the output is just one ID and success => 1.
How should I test it properly?
Some ideas on how to test small code pieces like this:
Generate test-data and pass it to your methods (also, fake database return data or file contents)
Use echo / var_dump() / die() to check property and variable content at different positions in your methods
Also use these commands to check whether execution reaches a certain point (for example to see whether a function got called or not)
If something doesn't work as expected without an error message: Check line by line with the above methods until you find the problem
Consider using interfaces and dependency injection if your code gets bigger - this is a bit over-the-top for this amount of code, but can be a tremendous time-saver when your application becomes big
Testing is never an automatic process and you will always have to think about what makes sense to do and what not. These things to do are never magic but basic PHP.
You should consider letting your scripts throw errors/exceptions if something goes wrong. Writing "silent" applications is almost never good since you can, if you really need a silent execution for production environments, just turn off error reporting and have the same effect. Many PHP functions return something special on failure and/or success and you can check for this. Database handlers do so, too. Do yourself a favor and use these return values!

Command pattern in php applications: how to handle controller actions?

I think this is more of a general question (so not php restricted) with regards to ddd and the command pattern.
Let's say I execute a CreatePostCommand from within the create action of my controller, the command will be handled and eventually executed successfully. What's the appropriate way to notify the controller which response to return in case the command did fail or succeed? Given the command handler will fire a domain specific event, I could hook up the controller to the event, but that seems a quite awkward, also not appropriate for every situation (e.g. a post could be created somewhere else and the controller really doesn't know about this :) ).
public function createAction($title, $content)
{
$this->commandBus->execute(new CreatePostCommand($title, $content);
$this->render('…'); // what if the command execution failed?
}
Any thoughts on this?
I think if you are really trying to follow the DDD command pattern then you need to treat the command bus as a fire and forget asynchronous process that may take a long time to complete.
Consider immediately redirecting to a command verifier controller. It's up to the command verifier to actively check the status of the command and see if it worked.
In most cases, the command will have finished successfully and your verifier can then redirect once again to continue normal flow.
If the command fails then the verifier puts up an appropriate error message.
If the command is in progress then you can entire a redirect loop while informing the user that the command is in progress.
Something like:
// Execute the command
$command = new CreatePostCommand($title, $content);
$this->commandBus->execute($command);
return redirect '/command-verifier/' . $command->getId();
// The verification action
public function verifyCommandAction($commandId)
$commandStatus = $this->commandBus->getStatus($commandId);
if ($commandStatus == SUCCESS) redirect to all is well;
if ($commandStatus == FAILED) then oops;
if ($commandStatus == IN_PROGRESS) then maybe pause a bit and redirect again while keeping the user informed.
Clearly there is quite a bit of hand waving going on but I think this is the most general approach especially with php where every request starts from ground zero.
The way I'm currently doing it is as follows (excuse long post).
public function createAction($title, $content) {
try {
$post = $this->commandBus->execute(new CreatePostCommand($title, $content);
}
catch (Exception $e) {
return $this->render('some error template file', $e);
}
return $this->render('successful creation template file', $post);
}
This way, you're creating a post and if everything goes as planned, return the $post object and send that to your view. On the other hand, when an exception is thrown during execution, you catch that error and send it to a view.
My preferred way is to have the controller call a method on a service that manages that behaviour, and have the controller injected as a listener that manages the responses, ie:
public function createAction($title, $content) {
$service = new CreateActionService($title, $content);
return $service->create($this);
}
public function onError(Exception $e) {
return $this->render('some error template file', $e);
}
public function onSuccess($post) {
return $this->render('success', $post);
}
Then in your service...
public function create($listener)
{
try {
$this->commandBus->execute(new CreatePostCommand($title, $content);
}
catch (Exception $e) {
return $this->listener->onError($e);
}
return $this->listener->onSuccess($post);
}
This way your service is managing the various results that the command handler may return, and your controller is left simply to manage the responses that you may wish returned to your presentation layer.

PHP variable loses its value

I have a really serious problem that I have not seen before.
On a website we are using opensource SQC eshop, PHP Version 5.3.3-7+squeeze15 and there is some kind of problem with variable memory I think.
SQC uses notORM and here the problem starts with fatal error "Call to function on non object notORMResult" .
So I dug deeper and found the constructor of NotORM that looks like this:
function __construct(PDO $connection, NotORM_Structure $structure = null,NotORM_Cache $cache = null) {
$this->connection = $connection;
if($_GET['test']){
var_dump($structure);
}
if (!isset($structure)) {
$structure = new NotORM_Structure_Convention;
}
if($_GET['test']){
var_dump($structure);
}
$this->structure = $structure;
if($_GET['test']){
var_dump($this->structure);
exit("1");
}
$this->cache = $cache;
}
And so the output is NULL because the constructor gets no structure param so we create an object. Second output is the object. Then we set the object to attribute and then the THIRD OUTPUT IS NULL
How is this even possible? The site was running for about year and half and no problems till yesterday. I didn't made yet any updates to php and this thing really freaks me out 'cause it's not a constant problem. It just happens sometimes after 2 hours, sometimes after 2 mins and I have really no idea why is this happening.
And btw ... this is just the start it happens across the whole script. Object attributes are set but when you want to read them they give you NULL. There is also second website running on the same server, same php same configuration without problem.
Thanks for any ideas :)

How do I run a PHPUnit Selenium test without having a new browser window run for each function?

I am trying to run a selenium test case using PHPUnit. And the first thing I do is trying the login function, this works perfect but then I want to run a function to check information on the page following the login but it opens a new browser instead of continuing in the current browser window. The reason this is a problem is because the page is setup to remove login authentication when the window is closed so if you use $this->url() to go to the page it gives the error that I need to login. This is my code right now, It starts the browser and runs the function to test the login form, then it closes the browser, open a new one and run the link check. This of course results in an error due to the authentication error because the window was closed. I could run all the tests in one function but that is really sloppy coding and I want to avoid this. Anyone know how to solve this?
<?php
class TestMyTest extends PHPUnit_Extensions_Selenium2TestCase {
public function setUp()
{
$this->setBrowser("firefox");
$this->setBrowserUrl("https://**************************");
}
public function testLoginForm()
{
$this->url("login.php");
$this->byLinkText('Forgot your password?');
$form = $this->byCssSelector('form');
$this->byName('username')->value('test');
$this->byName('password')->value('1234');
$form->submit();
}
public function testCheckForMainMenueLinks ()
{
$this->url("index.php");
$this->byLinkText('Home');
$this->byLinkText('Products');
$this->byLinkText('About us');
$this->byLinkText('Contact');
}
}
?>
To share browser sessions in Selenium2TestCase, you must set sessionStrategy => 'shared' in your initial browser setup:
public static $browsers = array(
array(
'...
'browserName' => 'iexplorer',
'sessionStrategy' => 'shared',
...
)
);
The alternative (default) is 'isolated'.
Okej so I guess you can just call the function directly from another function like so:
public function testOne
{
#code
$this->Two();
}
public function Two()
{
#code
$this->Three();
}
public function Three()
{
#code
}
and so on, this will just run the next function without a new browser, however, if it fails anywhere in any test the whole test is stoped so the feedback wont bee as good as individual tests.
make assetrions in one function because this is functional test.
i am new to phpunit and selenium too, but I successfully test all in one like this:
public function testAuth(){
$this->open('register.php&XDEBUG_SESSION_START=PHPSTORM');
$this->assertTextPresent('Register');
$this->type('name=email', "...");
$this->type('name=firstname', "...");
$this->type('name=lastname', "...");
$this->type('name=password', "...");
$this->type('name=verifyPassword', "...");
$this->click("reg-butt");
$this->waitForPageToLoad("5000");
$this->assertTextPresent('Profile');
$this->open('logout.php');
$this->assertTextPresent('text from redirect page');
$this->open('login.php');
.....
}
An elegant way to set the session shared is to use PHPUnit's setUpBeforeClass() method:
public static function setUpBeforeClass()
{
self::shareSession(true);
}
You can call PHPUnit_Extensions_SeleniumTestCase::shareSession(true) to enable browser window reuse.
In the manual it says:
From Selenium 1.1.1, an experimental feature is included allowing the user to share the session between tests. The only supported case is to share the session between all tests when a single browser is used. Call PHPUnit_Extensions_SeleniumTestCase::shareSession(true) in your bootstrap file to enable session sharing. The session will be reset in the case of not successul tests (failed or incomplete); it is up to the user to avoid interactions between tests by resetting cookies or logging out from the application under test (with a tearDown() method).

Preventing error pages caching when using Zend_Cache_Backend_Static

We're currently running an app that caches pages to static html files using Zend_Cache_Backend_Static. This works really well, except that our cache is getting filled with hundreds of empty files and folders when incorrect urls are requested. Is there any way to prevent a page being cached if an Exception is being thrown? I was surprised to discover that this wasn't standard behaviour.
I've done a little digging and the ZF code that actually deals with saving out the static html pages is as follows in Zend_Cache_Frontend_Capture:
public function _flush($data) {
$id = array_pop($this->_idStack);
if ($id === null) {
Zend_Cache::throwException('use of _flush() without a start()');
}
if ($this->_extension) {
$this->save(serialize(array($data, $this->_extension)), $id, $this->_tags);
} else {
$this->save($data, $id, $this->_tags);
}
return $data;
}
This function is the output_callback for ob_start. I've tried getting hold of the response object to test for status but it doesn't seem to work inside _flush.
$response = Zend_Controller_Front::getInstance()->getResponse();
if($response->getStatus() == '200') {
// do the save as normal
}
else {
// do nothing
return false;
}
My only other thought was to test the length of $data, only caching if strlen($data) > 0 seems to work but it doesn't feel robust enough.
Update:
Unfortunately by the time we hit the ErrorController the static page has already been written to the cache, so disabling the cache at that point won't work. However it is possible to remove the page based on $_SERVER['REQUEST_URI'], which is what is used as an id when the page is first written. This line can be added to the start of errorAction in the ErrorController:
$this->_helper->cache->removePage($_SERVER['REQUEST_URI'], true);
It works nicely, but I'd prefer not to write the page in the first place!
From further experimentation the problem is not down to standard Zend Framework exceptions that cause 404s (ie. Zend_Controller_Plugin_ErrorHandler::EXCEPTION_NO_ROUTE, Zend_Controller_Plugin_ErrorHandler::EXCEPTION_NO_CONTROLLER, Zend_Controller_Plugin_ErrorHandler::EXCEPTION_NO_ACTION) but to my custom exceptions. This is now really obvious now that I think about it, as Zend_Cache_Backend_Static needs to be initialised in the init method of an action controller. Any situation where there is no route, controller or action it won't ever be initialised anyway.
I'm throwing exceptions in existing actions where a user may be querying for a non-existent article. Therefore caching has been enabled in init and the page has been written by the time we hit postDispatch in a Front Controller Plugin (still not sure why this is the case it just is) so I can't cancel at that point. One solution then is to cancel the cache at the point of throwing the exception. The standard method of managing static page caching is using the Zend_Controller_Action_Helper_Cache action helper. I've extended this to add a cancel method like so:
<?php
class Zend_Controller_Action_Helper_PageCache extends Zend_Controller_Action_Helper_Cache {
public function cancel() {
$cache = $this->getCache(Zend_Cache_Manager::PAGECACHE);
$cache->setOption('caching', false);
$cache->getBackend('disable_caching', true);
}
}
My action controller now looks like this:
<?php
class IndexController extends Zend_Controller_Action {
private $_model;
public function init() {
$this->_model = new Model();
// using extended pageCache rather than $this->_helper->cache:
$this->_helper->pageCache(array('index'), array('indexaction'));
}
public function indexAction() {
$alias = $this->_request->getParam('article');
$article = $this->_model->getArticleByAlias($alias);
if(!$article) {
// new cancel method will disable caching
$this->_helper->pageCache->cancel();
throw new Zend_Controller_Action_Exception('Invalid article alias', 404);
}
$this->view->article = $article;
}
}
You should alter your .htaccess file RewriteRules to check for filesizes with option -s
This way if an error should occur when a page is being cached (thus producing a 0 byte file) it won't permanently be stored in the cache.
If you are using the standard ErrorController to handle 404, 500, and unhandled exceptions, and you can get a reference to your cache object from there, you could disable caching from the error handler.
In your error controller (or wherever you would like to cancel caching from), try:
$cache->setOption('caching', false);
When the save() metod of Zend_Cache_Core is called by Zend_Cache_Frontend_Capture::_flush(), it will see the caching option is set to false and it will not actually save the data to the cache and return true.

Categories