Laravel - Testing what happens after a redirect - php

I have a controller that after submitting a email, performs a redirect to the home, like this:
return Redirect::route('home')->with("message", "Ok!");
I am writing the tests for it, and I am not sure how to make phpunit to follow the redirect, to test the success message:
public function testMessageSucceeds() {
$crawler = $this->client->request('POST', '/contact', ['email' => 'test#test.com', 'message' => "lorem ipsum"]);
$this->assertResponseStatus(302);
$this->assertRedirectedToRoute('home');
$message = $crawler->filter('.success-message');
// Here it fails
$this->assertCount(1, $message);
}
If I substitute the code on the controller for this, and I remove the first 2 asserts, it works
Session::flash('message', 'Ok!');
return $this->makeView('staticPages.home');
But I would like to use the Redirect::route. Is there a way to make PHPUnit to follow the redirect?

You can get PHPUnit to follow redirects with:
Laravel >= 5.5.19:
$this->followingRedirects();
Laravel < 5.4.12:
$this->followRedirects();
Usage:
$response = $this->followingRedirects()
->post('/login', ['email' => 'john#example.com'])
->assertStatus(200);
Note: This needs to be set explicitly for each request.
For versions between these two:
See https://github.com/laravel/framework/issues/18016#issuecomment-322401713 for a workaround.

You can tell crawler to follow a redirect this way:
$crawler = $this->client->followRedirect();
so in your case that would be something like:
public function testMessageSucceeds() {
$this->client->request('POST', '/contact', ['email' => 'test#test.com', 'message' => "lorem ipsum"]);
$this->assertResponseStatus(302);
$this->assertRedirectedToRoute('home');
$crawler = $this->client->followRedirect();
$message = $crawler->filter('.success-message');
$this->assertCount(1, $message);
}

Laravel 8 tested
$response = $this->post'/contact', ['email' => 'test#test.com', 'message' => "lorem ipsum"]);
$response->assertStatus(302);
$response->assertRedirect('home');
$this->followRedirects($response)->assertSee('.success-message');
//or
$this->followRedirects($response)->assertSee('Ok!');
Worked for me, hoped it helps.

Since Laravel 5.5 to test redirect you can use assertRedirect:
/** #test */
public function store_creates_claim()
{
$response = $this->post(route('claims.store'), [
'first_name' => 'Joe',
]);
$response->assertRedirect(route('claims.index'));
}

//routes/web.php
Route::get('/', function () {
return redirect()->route('users.index');
})->name('index');
//on my TestClass
$response = $this->get('/');
$response->assertStatus(302);
$response->assertRedirect(route('users.index'));

For Laravel 5.6, you can set
$protected followRedirects = true;
within your class file for your test case

Related

Laravel Session Flash Returns Null After Using Artisan Cache Commands Inside Controller

I am trying to use a Session flash to return the success of clearing my websites cache. However, using the Artisan cache commands it seems that the session doesn't go through when returned back.
My controller function:
public function clearCache()
{
$flushCache = Cache::flush();
$clearCache = Artisan::call('cache:clear', ['--quiet' => true]);
$routeCache = Artisan::call('route:cache', ['--quiet' => true]);
$configCache = Artisan::call('config:cache', ['--quiet' => true]);
$viewCache = Artisan::call('view:cache', ['--quiet' => true]);
\Session::flash('alert', 'Site cache has been cleared');
return back();
}
I have tried both of these methods
\Session::flash('alert', 'Site cache has been cleared');
return back();
return back()->with('alert', 'Site cache has been cleared');
But after dd()'ing the results, it returns null.
However, if I remove these commands
$flushCache = Cache::flush();
$clearCache = Artisan::call('cache:clear', ['--quiet' => true]);
$routeCache = Artisan::call('route:cache', ['--quiet' => true]);
$configCache = Artisan::call('config:cache', ['--quiet' => true]);
$viewCache = Artisan::call('view:cache', ['--quiet' => true]);
Then it works perfectly fine. Thanks for your help in advance.
I know the question is old but I have had the same problem and it could be useful for someone.
Using Artisan to call config:cache, for some strange reason (to me), breaks the session and therefore the alert/success flash message does not appear.
I solved it by assigning the redirect to a variable before calling Artisan to return it afterwards, eg:
public function update(Request $request)
{
// do staff
$return = redirect()->route('...')->with('success', 'Update successfully!');
Artisan::call('config:cache');
return $return;
}

Calling a controller method from laravel middleware

I have a method in my base controller.php that formats all my responses to how I like it like so;
public function sendError($error, $errorMessages = [], $code = 404)
{
$response = [
'success' => false,
'message' => $error,
];
if (!empty($errorMessages)) {
$response['data'] = $errorMessages;
}
return response()->json($response, $code);
}
If I am calling it from another controller, i simply just call
return $this->sendError('Validation Error', $validator->errors(), 400);
But i am also using middleware for my JWT-Auth. Instead of re-writing the method, is there any way to call this controller method from inside middleware?
try this one in middleware by create of your controller
return (new yourChildController)->sendError('xyz errro',[],400)
First get the existing instance:
use Illuminate\Support\Facades\Route;
// ...
$myController = Route::getCurrentRoute()->getController();
Then call as you would normally, in OP's case:
return $myController->sendError('My error message.', [], 400);
Note that above is tested with Laravel 6.x release.

Testing message passed to view

How can I test that I get this message:
public function doSomething()
{
if($ok){
return view('message-page')
->with('title','The message?')
}
}
What can I assert to check the message that is passed to the view?
Updated:
Laravel 5.5
I think it is tricky because I am not doing a HTTP call, which would return a response. I am just doing a function call ($foo->doSomething();), so I don't get a response returned.
I can't do a GET because I need to pass in a mocked object. Here is my test so far:
public function do_the_test()
{
//Arrange
$mockObject = Mockery::mock('App\MockObject');
$authCode = 123456;
$mockObject->shouldReceive('doSomething')->once()->with($authCode)->andReturn([
'code' => '123456',
'name' => 'joe',
'username' => 'smith',
'email' => 'joe#yahoo.co.uk',
'domain' => 'yahoo.co.uk'
]);
//Act
$object = new Foo($mockObject);
$object->doSomething();
//Assert
??
//check that view is returned with message text
}
Progress:
I have hacked this by setting a session variable (instead of passing the messages with the view) and then checking that with assertEquals();
Would be nice to find a better way.
Not sure which laravel version you have, but still, you can use assertViewHas($key, $value) function:
public function testViewDoSomethingHasCorrectTitle()
{
$response = $this->call('GET', '/my_route');
$this->assertViewHas('title', 'The message?')
}
https://laravel.com/docs/5.5/http-tests#assert-view-has
just do a dd() and you'll know if it passed to the controller.
In your view do a {{ dd($title) }}

Dingo/API when Unit Testing: The version given was unknown or has no registered routes

I built an API using dingo/api 0.10.0, Laravel 5.1 and lucadegasperi/oauth2-server-laravel": "^5.1".
All my routes work fine in Postman/Paw!
The problem appears when I try to test the API using PHPUnit.
This is part of my route-api.php file
<?php
$api = app('Dingo\Api\Routing\Router');
$api->version(['v1'], function ($api) {
$api->post('oauth/access_token', function () {
return response(
\LucaDegasperi\OAuth2Server\Facades\Authorizer::issueAccessToken()
)->header('Content-Type', 'application/json');
});
$api->group(['middleware' => ['oauth', 'api.auth']], function ($api) {
$api->post('/register', 'YPS\Http\Controllers\Api\UserController#register');
});
And this is my test file UserRegistrationTest.php
class UserRegistrationTest extends ApiTestCase
{
public function setUp()
{
parent::setUp();
parent::afterApplicationCreated();
}
public function testRegisterSuccess()
{
$data = factory(YPS\User::class)->make()->toArray();
$data['password'] = 'password123';
$this->post('api/register', $data, $this->headers)
->seeStatusCode(201)
->seeJson([
'email' => $data['email'],
'first_name' => $data['first_name'],
'last_name' => $data['last_name'],
]);
}
public function testRegisterMissingParams()
{
$this->post('api/register', [], $this->headers, $this->headers, $this->headers)->seeStatusCode(422);
}
}
The ApiTestCase simply retrieves a token and sets the headers.
private function setHeaders()
{
$this->headers = [
'Accept' => 'application/vnd.yps.v1+json',
'Authorization' => 'Bearer ' . $this->OAuthAccessToken,
];
}
Now, the weird part is that the first test testRegisterSuccess runs perfectly and returns the response I expect. But the second one testRegisterMissingParams, even though it's the same route, returns this,
array:2 [
"message" => "The version given was unknown or has no registered routes."
"status_code" => 400
]
I tracked the error and it is in the Laravel adapter here:
public function dispatch(Request $request, $version)
{
// it seems that the second time around can't find any routes with the key 'v1'
if (! isset($this->routes[$version])) {
throw new UnknownVersionException;
}
$routes = $this->mergeExistingRoutes($this->routes[$version]);
$this->router->setRoutes($routes);
return $this->router->dispatch($request);
}
And further more, if i run one test at a time (eg comment one out, run test and then comment the other and run test) i see the result expected in both tests. The problem is when i run multiple tests.
Any thoughts on that?
Thank you!
Run php artisan api:routes to see full path you may have missed something for the URL, also if this working if you request your URL manually?
I had same problem with testing using Dingo & Lumen. This worked for me - remove bootstrap="bootstrap/app.php" from phpunit.xml file and change line processIsolation="false" to processIsolation="true".

No matching handler found for Illuminate\Routing\Redirector::to("user/login")

How do i go about mocking facades with arguments in laravel 4? For instance, i'm trying to test my user controller and in my 'login' method.
my controller method
public function login(){
$this->beforeFilter('guest');
$creds = array(
'email' => Input::get('email'),
'password' => Input::get('password'),
);
if(Auth::attempt($creds, true)){
return "successful";
} else {
return Redirect::to('user/login')->with('error', true);
}
}
the redirect test that doesn't work
public function testPostLogin(){
Redirect::shouldReceive('to')->once()->with('error', true);
$response = $this->action('POST', 'UserController#login');
$this->assertRedirectedTo('user/login');
}
I'm getting the following exception. I don't know how to inject the 'user/login' parameter into the Redirect mock
Mockery\Exception\NoMatchingExpectationException : No matching handler found for Illuminate\Routing\Redirector::to("user/login")
In theory you can just mock your Auth class.
Try this:
Auth::shouldReceive('attempt')->once()->andReturn(true);

Categories