Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

In Laravel 5, why is Request::root() different when called during phpunit test?

I defined a test which tests the creation of a user. The controller is set to redirect back to the same page on error (using validation through a generated App\Http\Requests\Request). This works correctly when manually clicking in a browser, but fails during a test. Instead of being redirected to:

http://localhost/account/create

The test redirects to (missing a slash):

http://localhostaccount/create

Neither of these urls are what I have setup in the .htaccess or in the $url variable in config/app.php. Which is (On OSX Yosemite):

http://~username/laravel_projects/projectname/public

I finally pinpointed the issue to have something to do with how the result of Request::root() is generated. Making a call to this outside of a test results in the expected value defined in .htaccess and $url. Inside the test it results in:

http://localhost

What configuration needs to change in order to get this function to return the correct value in both contexts?

I should also mention I made the painful upgrade from Laravel 4 to the current version 5.0.27.

****** UPDATE *******

I was able to figure out an acceptable solution/workaround to this issue!

In Laravel 5, FormRequests were introduced to help move validation logic out of controllers. Once a request is mapped to the controller, if a FormRequest (or just Request) is specified, this is executed before hitting the controller action.

This FormRequest by default handles the response if the validation fails. It attempts to construct a redirect based on the route you posted the form data to. In my case, possibly related to an error of mine updating from Laravel 4 to 5, this default redirect was being constructed incorrectly. The Laravel System code for handling the response looks like this:

 /**
 * Get the proper failed validation response for the request.
 *
 * @param  array  $errors
 * @return \Symfony\Component\HttpFoundation\Response
 */
public function response(array $errors)
{
    if ($this->ajax() || $this->wantsJson())
    {
        return new JsonResponse($errors, 422);
    }
    return $this->redirector->to($this->getRedirectUrl())
                                    ->withInput($this->except($this->dontFlash))
                                    ->withErrors($errors, $this->errorBag);
}

Notice how the returned redirect is NOT the same as calling Redirect::route('some_route'). You can override this response function by including use Response in your Request class.

After using Redirect::route() to create the redirect, the logic in my tests passed with the expected results. Here is my Request code that worked:

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;
use App\Http\Requests\Request;
use Response;

class AccountRequest extends FormRequest {

    /**
     * Determine if the user is authorized to make this request.
     *
     * @return bool
     */
    public function authorize()
    {
        return true;
    }

    /**
     * Get the validation rules that apply to the request.
     *
     * @return array
     */
    public function rules()
    {
        return [
                  'email' => 'required|max:50|email|unique:users',
                  'password' => 'required|min:6',
                  'password_confirmation' => 'required|same:password'

                ];
    }

  public function response(array $errors){
    return \Redirect::route('account_create');
  }

}

The important part is that I called Redirect::route instead of letting the default response code execute.

like image 673
kurt165749 Avatar asked Apr 07 '15 04:04

kurt165749


1 Answers

Override the response function in the FormRequest validation handler to force the redirect to be constructed with Redirect::route('named_route') instead of allowing the default redirect.

like image 127
kurt165749 Avatar answered Oct 02 '22 03:10

kurt165749