I'm writing some unit tests for a Controller in which the response behavior differs slightly, depending on whether $this->request->is('ajax'). I've been looking over the documentation for testing controllers, but I can't seem to find a way to simulate a request made via AJAX.
Is there a way to send headers to testAction() so I can set the X-Requested-With header?
Edit: You can work around this by editing the superglobals. Anyone have a less hacky solution?
One solution is to manually declare the necessary environment variable for the duration of your test:
$_ENV['HTTP_X_REQUESTED_WITH'] = 'XMLHttpRequest';
// Run your AJAX test...
unset($_ENV['HTTP_X_REQUESTED_WITH']);
Related
I am currently busy with a PSR-7 project with responses and requests.
Currently we are setting up an application in our index.php by doing something like:
$app = new Application();
$app->loadConfiguration(
'../config/global.yml',
);
// Should return the response?
$app->run((new ServerRequestFactory())->createServerRequestFromGlobals());
Here the run method also calls an emit method that is responsible for sending the headers and printing the body of the response.
The request and respons are now linked together in one call which makes it hard to test since you don't want to send the response with the headers straight to PHPUnit.
I have removed the emit call in the chain of the run method and added this to the index after the run method call:
// Send the response.
$app->send();
This way they are decoupled but the downside is I now have to hold a instance of my response in a response property inside my Application.php($app) class.
I want to move the response instance to the response class itself but my co-workers thinks a class should never hold an instance of itself. Yet when I look at frameworks this happens quite a lot. Is he right about this?
What arguments can I make to decouple my request and response besides easier testing?
I am pretty new to unit testing, one of the arguments I have already heard is that I should not test the full application anyways but rather separate components and therefore should not be worried about de-coupling the request and response.
I'd like to be able to manage WebDAV directories (and even reimplement the way files are read and written) in Symfony. To do so I found SabreDAV, which is itself a framework with all the basic classes required.
My problem is, while it's quite easy to get a WebDAV server running using SabreDAV alone, it doesn't work that well when I use Symfony.
Without Symfony, it boils down to:
$server = new DAV\Server($rootDirectory);
$server->exec();
And I can use cadaver to access my directory.
More here: http://code.google.com/p/sabredav/wiki/GettingStarted
I tried to do the same in my controller with Symfony, using:
return new Response($server->exec());
but for some reason cadaver doesn't have access to the folder.
I guess I'm missing something about the way responses work in Symfony, but what? SabreDAV uses its own system of http requests and responses, but if (as I presume) Symfony doesn't mess with superglobal variables such as $_SERVER, this shouldn't be an issue.
About requests and responses in Symfony: http://symfony.com/doc/current/book/http_fundamentals.html#requests-and-responses-in-symfony
Here's what I did; it's a bit slow and there must be a better way, but I'll make do with that for the moment:
Controller.php :
$path=(__DIR__.'/../../../../web/public/');
$path=realpath($path);
$publicDir= new \MyClasses\FS\MyDirectory($path);
$server = new \Sabre\DAV\Server($publicDir);
$server->setBaseUri('/Symfony/web/app_dev.php/');
{
$SyRequest = Request::createFromGlobals();
$_server=$SyRequest->server->all();
$_post=$SyRequest->request->all();
}
{
$SaRequest=new \MyClasses\HTTP\Request($_server,$_post);
$resourceStream=false;
$SaRequest->setBody($SyRequest->getContent($resourceStream),$resourceStream);
}
{
$server->httpRequest=$SaRequest;
$SaResponse=new \MyClasses\HTTP\Response();
$server->httpResponse=$SaResponse;
$server->exec();
}
{
$content=ob_get_clean();
}
{
$SyResponse=new Response($content,http_response_code(),headers_list());
}
return $SyResponse;
$server->exec();
Doesn't really return anything. It attempts to set headers itself, and stream the output to php://output (indeed, with the built-in request/response system).
If you want to embed SabreDAV into symfony, the most proper way to solve this is to subclass both Sabre\HTTP\Request and Sabre\HTTP\Response, and set these in the server (setting the ->httpRequest and ->httpResponse properties) before calling ->exec.
Your overridden request/response objects should then map to symfony's equivalents.
I don't know enough about symfony to tell you if they map cleanly and easily though, and I imagine it will in practice be simpler to try to work around symfony's system (although from an architectural standpoint, it will not be the most proper).
When I'm trying to execute a functional unittest of a module within my Yii code, I keep receiving the following error:
CException: CHttpRequest is unable to determine the request URI.
At first, I though it was because it couldn't find the module. However, If I change the url to a wrong one, I get a correct error,s tating it couldn't find the view.
This is how my testing code looks like
public function testViewControllerModule()
{
ob_start();
Yii::app()->runController('module/controller/view');
}
Any ideas on what I might be missing?
bool.devs answer works so far.
This blog post explains the origin of the exception pretty well:
http://mattmccormick.ca/2012/09/14/unit-testing-url-routes-in-yii-framework/
In my case, I generalized the solution and have set the following variables in /www/protected/tests/bootstrap.php:
...
$_SERVER['SCRIPT_FILENAME'] = 'index-test.php';
$_SERVER['SCRIPT_NAME'] = '/index-test.php';
$_SERVER['REQUEST_URI'] = 'index-test.php';
Yii::createWebApplication($config);
Consider using 'index-test.php' instead of 'index.php' because it contains the config 'test.php' which is responsible for fixtures and maybe other test relevated configurations.
If someone has better suggestions feel free to comment :)
Kind regards
I think it's because you haven't set any server variables, i.e $_SERVER and you might be doing something like this in your controller:
Yii::app()->request ....
So before you run your test, make sure you use a fixture for the server variables also. I think this should suffice for now:
$_SERVER=array(
'REQUEST_URI'=>'index.php', // the other fields should follow
);
However to run functional tests i would recommend using SeleniumRC, you won't have to do these workarounds then, and can simulate user clicks also, i think.
Read the initial guide to Functional Testing , read the selenium rc phpunit guide, and also the CWebTestCase documentation.
Notes: You might still have to use fixtures for some variables, and i don't have much experience in testing(which is bad), so i'm not very sure if i am completely correct about selenium.
I can't work this out.
$this->_request->setBasePath('http://localhost/');
$this->_request->setModuleKey('admin');
$this->_request->setControllerKey('controller-page');
$this->_request->setActionKey('index');
$this->_sslRedirect->preDispatch($this->_request);
$this->assertRedirectRegex('/https:/');
In my controller plugin preDispatch() may or may not do a redirect to SSL. This works in the browser, however I'm not able to write a test for it. The test fails, like so:
Failed asserting response redirects to URL MATCHING "/https:/"
/srv/app/zend/library/Zend/Test/PHPUnit/Constraint/Redirect.php:190
/srv/app/zend/library/Zend/Test/PHPUnit/ControllerTestCase.php:764
/srv/app/www/tests/lib/Saffron/Controller/Plugin/SslRedirectTest.php:36
/usr/bin/phpunit:46
I also tried with a $this->dispatch() inside the Zend_Test_PHPUnit_ControllerTestCase immediately after the preDispatch() line, but for some reason this caused a Error 404 Not Found...
Any ideas?
If this is your unit test code, I'm not seeing anything mocked up, and I'm completely sure I understand why this is being implemented this way (if the goal is to force an https connection)
I think theres an issue with these two lines.
$this->_sslRedirect->preDispatch($this->_request);
$this->assertRedirectRegex('/https:/');
If you could post the complete action you are testing, and the complete unit test you are using to test it, it would help.
Should I extend the ControllerTestCase and create a custom method to handle this? What is the best way?
AJAX response should be no different than any other response. So no subclassing should be necessary.
But if you mean "how to test that the response AJAX request generated from the file I'm testing is correct?" there is no way to do that (except Selenium tests maybe). And it's not PHP's job to test this - you should use JavaScript tests to test it.
When you're sure the AJAX is calling correct URL with correct params, you can create the AJAX request artificaly and test the response as normal request.