@taylorotwell
Laravel\Lumen\Testing\ApplicationTrait doesn't seem to be working as expected\intended. In summary:
It seems I'm not alone with this:
Code:
routes.php:
$app->get('/', ['as' => 'getIndex', function() use ($app)
{
$request = $app->make(\Illuminate\Http\Request::class);
dd($request->all());
}]);
FooTest.php
public function test_foo_bar()
{
$response = $this->call('GET', '/', ['foo' => 'bar']);
}
It seems like I remember fixing something like this in Laravel full stack
at some point.
On Sunday, April 26, 2015, Nik [email protected] wrote:
@taylorotwell https://github.com/taylorotwell
Laravel\Lumen\Testing\ApplicationTrait doesn't seem to be working as
expected\intended. In summary:
- Hitting routes via browser/curl works as expected, with request
parameters being present.- Hitting routes via unit testing methods does _not_ work as expect,
with request parameters being _empty_.It seems I'm not alone with this:
-
http://laravel.io/forum/04-19-2015-lumen-requestall-is-empty-when-using-this-call-in-unit-test
- https://laracasts.com/discuss/channels/testing/lumen-unit-testing
-
http://stackoverflow.com/questions/29826362/testing-a-post-request-with-specific-parameter-in-lumen
-
https://laracasts.com/discuss/channels/general-discussion/i-cant-test-a-post-request-in-lumen?page=1Code:
routes.php:
$app->get('/', ['as' => 'getIndex', function() use ($app){ $request = $app->make(\Illuminate\Http\Request::class); dd($request->all());}]);
FooTest.php
public function test_index_doesnt_redirect(){ $response = $this->call('GET', '/', ['foo' => 'bar']);}
—
Reply to this email directly or view it on GitHub
https://github.com/laravel/lumen-framework/issues/55.
@taylorotwell After a bit of digging, below are a few code points that might be relevant. The bottom-most one seems to show that the $request object stops being used altogether at some point. Its path and method are taken, and that's it.
First, call() itself...
// Lumen's ApplicationTrait
public function call($method, $uri, $parameters = [], $cookies = [], $files = [], $server = [], $content = null)
{
$request = Request::create($uri, $method, $parameters, $cookies, $files, $server, $content);
return $this->response = $this->app->prepareResponse($this->app->handle($request));
}
// Laravel's ApplicationTrait
public function call($method, $uri, $parameters = [], $cookies = [], $files = [], $server = [], $content = null)
{
$request = Request::create($uri, $method, $parameters, $cookies, $files, $server, $content);
return $this->response = $this->app->make('Illuminate\Contracts\Http\Kernel')->handle($request);
}
And, Lumen's dispatch(), which is called by handle(), above...
// \Laravel\Lumen\Application
public function dispatch($request = null)
{
if ($request) {
$method = $request->getMethod();
$pathInfo = $request->getPathInfo();
} else {
$method = $this->getMethod();
$pathInfo = $this->getPathInfo();
}
try {
if (isset($this->routes[$method.$pathInfo])) {
return $this->handleFoundRoute([true, $this->routes[$method.$pathInfo]['action'], []]);
}
return $this->handleDispatcherResponse(
$this->createDispatcher()->dispatch($method, $pathInfo)
);
} catch (Exception $e) {
return $this->sendExceptionToHandler($e);
}
}
Something funky is also happening with the HTTP request method.
If I put the following in my controller for a POST route, it returns GET when unit testing but POST for normal curl requests.
dd($request->getMethod());
Fixed in Lumen 5.0.8.
On Mon, Apr 27, 2015 at 9:20 AM, Mathias Hansen [email protected]
wrote:
Something funky is also happening with the HTTP request method.
If I put the following in my controller for a POST route, it returns GET.
dd($request->getMethod());
—
Reply to this email directly or view it on GitHub
https://github.com/laravel/lumen-framework/issues/55#issuecomment-96671934
.
Awesome! My tests are all passing now without any issues.
Thanks!
Same, thanks @taylorotwell ! :tada: Closing.
Hello, I have same problem using latest lumen version.
My code in test:
$this->headers = [
'HTTP_ACCEPT' => 'application/vnd.' . env('API_VENDOR', '') . '.v1+json',
'HTTP_AUTHORIZATION' => 'Bearer ' . $jwt
];
$request = $this->get('api/user?access_token='.$jwt, $this->headers);
both header and query params are lost. can you help me?
the same to Eke
I'm using Lumen 5.2.9 and in my tests i'm also experiencing the problem that my query parameters get lost.
$getResponse = $this->call(
'GET',
'/v1/users/me?include=matches',
[],
[],
[],
$this->token
);
and also when using the call like this
$getResponse = $this->call(
'GET',
'/v1/users/me',
['includes' => 'matches'],
[],
[],
$this->token
);
I have the same problem with query parameters. (Lumen 5.3)
same here. query parameters lost, header parameters lost, POST end up in GET, ....
(lumen 5.3.3)
Same goes for Lumen 5.4.0. The request doesn't seem to have anything bound to it.
When I try to test an API endpoint with the following testcase:
<?php
class GroupControllerTest extends \TestCase
{
public function testCreateGroup()
{
$account = $this->faker->uuid;
$this->json('POST', "/v1.0/$account/groups", $data = [
'name' => $this->faker->name
]);
$this->seeJsonContains($data);
$this->assertResponseStatus(201);
}
}
The request seems to be empty inside the actual controller.
<?php
namespace App\Http\Controllers;
class GroupsController extends Controller
{
public function create(Request $request, $account)
{
// $request->all() is an empty array
return response()->json($request->all(), Response::HTTP_CREATED);
}
}
I can replicate this.
I'm not sure why but putting app(Request::class); before the test is run seems to resolve the problem:
<?php
use Illuminate\Http\Request;
abstract class TestCase extends Laravel\Lumen\Testing\TestCase
{
/** @var \Faker\Generator */
protected $faker;
/**
* Creates the application.
*
* @return \Laravel\Lumen\Application
*/
public function createApplication()
{
return require __DIR__.'/../bootstrap/app.php';
}
public function setUp()
{
parent::setUp();
app(Request::class);
}
}
I have the same problem with Lumen 5.4 ,phpunit test parameters lost.when use postman or browser its ok.
phpunit test code
public function teststore_should_save_new_book_in_the_database()
{
$this->post('/books', [
'title' => 'The Invisible Man',
'description' => 'An invisible man is trapped in the terror of his own creation',
'author' => 'H. G. Wells']
);
$this->seeJson(['created' => true])
->seeInDatabase('books', ['title' => 'The Invisible Man']);
}
controller code
public function store(Request $request)
{
dd($request->all());
try{
$book = Book::create($request->all());
}catch (\Exception $e){
dd(get_class($e));
}
return response()->json(['created' => true], 201);
}
@TFarla this problem fixed in lumen 5.4.2.
@lzpfmh Still noticing the issue in 5.4.4
The issue is that MakesHttpRequests::call does not create a request using createFromBase which would load the json params. #577 fixes it.
Still not working on Lumen 5.4.5
Laravel Framework Lumen (5.4.5) (Laravel Components 5.4.*)
###
PHPUnit 5.7.14 by Sebastian Bergmann and contributors.
Runtime: PHP 7.0.15-0ubuntu0.16.04.2
Configuration: /var/www/***/phpunit.xml
."GET"
[]
@drekinov I just run a simple test and it does work in 5.4.5.
$app->post('/test', function(Request $request) {
dd($request->all());
});
Result using Postman POST http://screen.ac/0A2E423o0x45
Result in test using http://screen.ac/1V2U290M1X2U
$response = $this->json('POST', 'test', [
'test_name' => 'Mary',
'test_phone' => '999-888-7777'
]);
dd($response->getContent());
You need to use $this->json() in a test to be sending json data or $this->call() and pass in content and content-type: application/json header ($this->json() just does that for you).
@cristiangrama you are right. clean installation of Lumen 5.4.5 works.
I was little frustrated frustrated while writing the comment but finally find out the truth and edit the comment :)
Lumen 5.4 upgrade guide says that Request should not be touch in ServiceProviders. Probably when request is accessed in SP it break the tests ?
We trace where the POST request get replaced by GET without parameters. In container there was no alias between Request::class and request so tests create new empty request object. Following works:
## TestCase.php
public function createApplication()
{
$app = require __DIR__ . '/../bootstrap/app.php';
$app->alias(\Illuminate\Http\Request::class, 'request');
return $app;
}
If you are wondering why we want to access Request in ServicProvider: our test case is Twilio application which interacts via twiml with twilio and guide the phone calls so we need some request interactions in service providers so we can load call specific data in container. I guess that there are a lot cases when Request is required for different APIs to load objects in container specific for current request.
@taylorotwell I am seeing this in Laravel 5.4.23 when using $this->post(...) instead of $this->json(...). The reason I want to explicitly use post is so I can validate against the headers. json supplies the correct Content-Type and Accept headers automatically and I don't want that.
Hi,
Lumen 5.4.7 affected.
Same issue with both json and post IF you're type checking against anything other than \Illuminate\Http\Request in your controller
Hi,
Same issue with Lumen (5.4.7) (Laravel Components 5.4.*).
Example:
$this->json('POST', '/users/login', [
'username' => 'rummykhan',
'password' => 'rummykhan',
]);
Solved the same issue - in my case problem was with test data because json_encode returned FALSE.
You can see this function in the MakesHttpRequests.php:
public function json($method, $uri, array $data = [], array $headers = [])
{
$content = json_encode($data);
$headers = array_merge([
'CONTENT_LENGTH' => mb_strlen($content, '8bit'),
'CONTENT_TYPE' => 'application/json',
'Accept' => 'application/json',
], $headers);
$this->call(
$method, $uri, [], [], [], $this->transformHeadersToServerVars($headers), $content
);
return $this;
}
And the first thing it's doing is json_encode array with testing data to be posted. So I fixed my test scenario data so that json_encode is no more FALSE and now tests works fine.
Same issue with Lumen 5.5.2, type hinting child classes from Illuminate\Http\Request looses content, params, etc while testing API.
Opened another bug ticket: https://github.com/laravel/lumen-framework/issues/694
Same issue here with Lumen 5.5.2, only when testing:
// Sending a request from a test.
public function it_does_something()
{
$this->json('GET', '/api/v1/something', [
'from' => '2017-01-01',
'to' => '2017-12-31',
]);
}
// Dumping from my JSON resource.
public function toArray($request)
{
dd(
// []
$request->all,
// ['from' => '2017-01-01', 'to' => '2017-12-31']
app('request')->all()
);
}
This appears to be an ongoing issue even within Laravel 5.5.
Although, in my instance, I may have narrowed the problem down to the fact that phpunit isn't actually performing any http requests and is instead calling parts of the system directly. (Please don't quote me on that though).
I came across this when I noticed the below block being skipped over for phpunit tests:
public function __construct(Request $request)
{
if (!\App::runningInConsole())
{
// Never accessed
});
I have this check on almost every single one of my controllers to prevent artisan (route:list springs to mind straight away) from picking up __constructs() in it's calls.
Downside to this is that checking php_sapi_name() as per
\Illuminate\Foundation\Application::runningInConsole()
shows "cli" which of course isn't overly unique. I'm not sure if there is a clean way to determine if phpunit is the calling process but this will most likely solve plenty of issues in my code.
I'm hoping this helps anybody (or somebody) narrow down the issue as it's rather frustrating at present.
I certainly seem to be experiencing similar issues to this. I've explained in more detail at https://stackoverflow.com/questions/49856295/laravel-phpunit-test-with-json-request-loses-important-request-details
I really don't understand how this can be happening without breaking considerably more of my code.
This issue shouldn't be closed. It persists to this day in the most recent version of Lumen.
@themsaid since you closed this, I suppose you have a recommended workaround or fix. Will you share it with us, please?
@cdarken since this issue is 4 years old I think it's better to open a new issue with new steps of replication on a fresh laravel 5.6 app.
@tholder I've got the exact same thing going on with my tests that you linked to.
Same here. In my lumen installation, I created a unit test, in which I created a new Request object and added post parameters within the test. And my test fails as the ->all() returns an empty array. Has anyone opened a new issue?
@jobnomade The last ticket about this issue, that i can find, is from december 2017.
Thanks. Would be good to see if anyone is working on this. I mean it is a lightweight framework to build microservices. And we can't even test a request on the console?
Same issue. Works fine if I use Request in my controller, but if I use a subclass of Request, the ParameterBag is empty. Really unlike the Laravel team to let this go for over a year 😕
Lumen: 5.6.4
To reproduce:
// Controller
class MyController extends Controller
{
public function myMethod(MyCustomRequest $request)
{
// dd($request->json()); <--- This outputs an empty ParameterBag object
return response()->json(['foo' => $request->json('foo')], 200); // <--- Should return {"foo": "bar"}
}
}
// Test
class MyControllerTest extends TestCase
{
public function testMyMethod()
{
// This test fails
$this->json('POST', '/the-uri', ['foo' => 'bar'])->seeStatusCode(200)->seeJson(['foo' => 'bar']);
}
}
@jobnomade Did you ever find a workaround for this?
Anyone have a solution for this ?
@benjamincrozat You're making a JSON request on GET which I don't think will work as you expect it. Try with POST or PUT.
@benwilkins I think your problem is coming from here https://github.com/laravel/lumen-framework/blob/5.6/src/Concerns/RoutesRequests.php#L186.
If you dump the actual request object in your controller while using a real api call (not from your test)
public function myMethod(MyCustomRequest $request)
{
dd($request);
}
you'll see that it does not look like it was instantiated properly http://screen.ac/580a3586c742 (missing, content, route, method, etc). However, dumping the json will still give you the right content. I suppose there's some magic happening somewhere that pulls the right content from the Request class considering that your custom request class extends from Request.
@benwilkins I have checked Illuminate\Http\Request and found the merge() method which comes closest to the solution I wanted. So after I applied the merge() method, the request inputs are added and I can call them and all() provides the expected output. However check whether the merge() function does not really merge or override any other input fields from you.
I'm using replace request params in TestCase.php
/**
* Call the given URI and return the Response.
*
* @param string $method
* @param string $uri
* @param array $parameters
* @param array $cookies
* @param array $files
* @param array $server
* @param string $content
* @return \Illuminate\Http\Response
*/
public function call($method, $uri, $parameters = [], $cookies = [], $files = [], $server = [], $content = null)
{
//Fix issue with request params not existent
$this->currentUri = $this->prepareUrlForRequest($uri);
$symfonyRequest = SymfonyRequest::create(
$this->currentUri, $method, $parameters,
$cookies, $files, $server, $content
);
/**@var Request $request*/
$request = $this->app['request'] = Request::createFromBase($symfonyRequest);
$request->replace($parameters);
return $this->response = $this->app->prepareResponse(
$this->app->handle($this->app['request'])
);
}
Hi Guys,
I am having a same problem, posted question on laracase. does anyone has solution?
https://laracasts.com/discuss/channels/lumen/lumen-phpunit-lost-post-data
@danyal14 I've just had to fix this bug on a Lumen 5.5 deployment. Whilst not a solution, this seems to be fixed from Lumen >5.7. As 5.5 is several versions behind the latest, I'd recommend updating!
I am experiencing this even in version 6.x. But I found a possible solution for my case. I hope I can help you.
app('request')->json()
While, for some reason, app(\Laravel\Lumen\Http\Request::class)->json() will not works consistently between phpunit and browser.
I can confirm this issue on Lumen 6.4. After hours debugging and trying a lot of different stuffs, I have decided to create a fresh clean project to test this issue again. With a fresh Lumen 7 installation everything was working as expected.
Since we had a Lumen 5 project that was upgraded to Lumen 6.4 by my friend, I'm assuming he did a mistake or forgot something while upgrading. If I find out what was causing the issue I will post here.
app(Request::class);
I am having the same issue and app(Request::class); seems to fix it. Now my all test passes.
I'm running into this exact error but with a specific endpoint of an actual Laravel app that was upgraded from v6.x to v8.x, and the same symptoms are visible. The request method is reported as GET and there is no input data in the type-hinted Request parameter. I spent a couple hours stepping through the request setup process with xdebug practically line by line and found the point at which everything seems to go wrong to be this line: https://github.com/laravel/framework/blob/0b989c5c5071e6dc930b22a212d484562ee0bee3/src/Illuminate/Routing/Router.php#L690
At this point the $request's value has all my parameters and the correct POST method in it. Setting a breakpoint there, then stepping through into everything eventually takes me to here: https://github.com/laravel/framework/blob/0b989c5c5071e6dc930b22a212d484562ee0bee3/src/Illuminate/Routing/RouteDependencyResolverTrait.php#L39
And after this chain the execution ends up in https://github.com/laravel/framework/blob/0b989c5c5071e6dc930b22a212d484562ee0bee3/src/Illuminate/Container/Container.php#L690
Setting a conditional breakpoint here that fires when $concrete === "Illuminate\HTTP\Request" clearly shows that a blank request object ends up being created when the parameter is being resolved, and that blank request object is what then gets passed to the controller instead of the one from earlier.
What's happening here is beyond me, literally no other endpoint of mine is affected by this, no matter what I name it or change its path to. I'll try to create a reproduction for this as it's currently preventing me from upgrading my app and if I manage to get it to happen reliably I'll create a bug report in the Laravel repository as it's likely an issue present in the main framework itself and not Lumen, or something to do with a 3rd-party dependency throwing it off.
So after countless hours of debugging it turns out I narrowed the issue down to user error. I generated a blank project, recreated the controller by using php artisan make:controller then adding all the same methods and lo and behold it worked. So I diffed the two controllers.
My controller had the following line at the top:
use Illuminate\HTTP\Request;
The fresh one made by Laravel had:
use Illuminate\Http\Request;
As soon as I changed my controller to use the correct casing the reference resolved properly. This was a valuable lesson for sure, and I'm not sure how this was never caught before.
So after countless hours of debugging it turns out I narrowed the issue down to user error. I generated a blank project, recreated the controller by using
php artisan make:controllerthen adding all the same methods and lo and behold it worked. So I diffed the two controllers.My controller had the following line at the top:
use Illuminate\HTTP\Request;The fresh one made by Laravel had:
use Illuminate\Http\Request;As soon as I changed my controller to use the correct casing the reference resolved properly. This was a valuable lesson for sure, and I'm not sure how this was never caught before.
Thank you. This solved my problem.
Most helpful comment
I'm not sure why but putting
app(Request::class);before the test is run seems to resolve the problem: