How to properly call laravel's middleware closure in phpunit? - php

How to properly call this middleware closure function in phpunit test so that $user will be set?
Auth::user is mocked and will receive the proper user object, but the function is not called when creating the controller instance.
Here is my code:
use App\Http\Controllers\Controller;
class CustomController extends Controller
{
private $user;
public function __construct()
{
$this->middleware(function ($request, $next) {
$this->user = Auth::user();
return $next($request);
});
}
}
Phpunit:
use Illuminate\Foundation\Testing\TestCase;
use Illuminate\Support\Facades\Auth;
use App\Models\User;
use App\Controllers\CustomController;
class CustomControllerTest extends TestCase
{
use CreatesApplication;
private $customController;
public function setUp()
{
Auth::shouldReceive('user')->andReturn(new User());
$this->customController = $this->app->make(CustomController::class);
}
}

You are resolving the controller out of the container, but then you aren't passing it any requests to handle. Your middleware code will only be executed when the controller is given something to handle.
As such, $this->middleware(...) simply says "execute this middleware when a request is sent through the pipeline".
Simply test a standard request hitting your endpoint, and then assert what you need to assert.
$this->get('your_endpoint');

Related

Laravel: Is there a way to reuse a method that is using a Request variable as parameter?

I want to reuse my method store that is in generar\productoController
public function store(Request $request){}
and I want to reuse it in this class adquisicion\ComprasController, I know that I have to import the class to use the method i want, but the problem is the $request variable, should I create a new object of it with $request = new Request(), adding the data I want with this and sending it as parameter?
Thx for the help I'm really new with laravel
you can try it like this $this->store(request(),$otherData)
use the helper to get the current object of request
You can pass Request data to other method
productoController(Request $request){
// anything here
return redirect('your route name')->with('data', $request->all());
}
Here are two ways that can make methods reusable in laravel application:
Make a helper method
Create a Helpers folder in app folder, and create all static methods inside a helper.php
Helper.php
namespace App\Helpers;
class Helper {
public static function store() {
$request = request();
// ....
}
}
YourController.php
namespace App\Repositories;
use App\Helpers\Helper;
use Illuminate\Http\Request;
class YourController extends Controller
{
public function store(Request $request) {
// call the store method as
Helper::store();
}
}
The downside here is you will mix up all the non-related helper methods here and may difficult to organize.
Repository
You can use a Repository Pattern to architect your application, for example, if you store a foo object to your datastore, then you can first create Repositories folder in app folder, and create FooRepository.php in Repositories folder:
FooRepository.php
namespace App\Repositories;
class FooRepository {
public function store() {
$request = request();
// ...
}
}
YourController.php
namespace App\Http\Controllers;
use App\Repositories\FooRepository;
use Illuminate\Http\Request;
class YourController extends Controller
{
private $fooRepository = null;
public function __construct(FooRepository $fooRepository) {
parent::__construct();
$this->fooRepository = $fooRepository;
}
public function store(Request $request) {
// call the method as
$this->fooRepository->store();
}
}

Laravel - Setting auth user inside controller constructor

Is it bad practice for me to set the auth user inside my base controller as follows:
abstract class Controller extends BaseController
{
use AuthorizesRequests, DispatchesJobs, ValidatesRequests;
protected $user;
public function __construct()
{
$this->middleware(function ($request, $next) {
$this->user = Auth::user();
view()->share('signedIn', Auth::check());
view()->share('user', $this->user);
return $next($request);
});
}
}
Or should I be accessing the auth user inside my controller methods as follows:
public function index()
{
$user = Auth::user();
}
I'm asking based on taylors comments (https://laravel-news.com/controller-construct-session-changes-in-laravel-5-3)
However Jeffery Way seems to be setting this in the constructor as demonstrated in a number of lessons on Laracast?
In newer versions, you can't work with session inside controller constructor as you could in 5.2. You're just watching old lessons.
Also, define middleware in its own class, not in a controller.

How to get logged in user in a laravel controller constructor

What I want to do is to get the a User's activation status before running any methods and redirect if they're not active. Here's my code:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\User;
class HomeController extends BaseController
{
public function __construct(){
parent::__CONSTRUCT();
$this->middleware('auth');
//SEE IF ACTIVE, something like auth()->user()->active
}
public function home()
{
return redirect('/home');
}
}
Look at the comment on the last line of the constructor, how do I do that?
From 5.3 onwards, you can't directly access session info in a controllers constructor. You can, though, define a Closure based middleware directly in your controller's constructor. More info in the docs
public function __construct()
{
$this->middleware('auth');
$this->middleware(function ($request, $next) {
if(Auth::user()->active) {
return Redirect::route('activate');
}
return $next($request);
});
}

Laravel 5 : Is it possible to use __construct() and Illuminate\Http\Request for testcase classes?

I'm trying dependency injection for test cases, wondering if it is possible to do that for testing.
<?php
namespace App\Http\Controllers;
use App\Http\Controllers\Controller;
use App\User;
use Illuminate\Http\Request;
class PageController extends Controller
{
protected $request;
protected $user;
public function __construct(Request $request, User $user)
{
$this->user = $user;
$this->request = $request;
}
This is how I usually do dependency injection.
For example, the following line gets an array instance of all the users.
$users = $this->user->all();
I'd like to do the same thing for 'TestCase'. Here is an excerpt of the code.
use App\User;
use Illuminate\Http\Request;
class ViewTest extends TestCase
{
protected $request;
protected $user;
public function __construct(Request $request, User $user)
{
$this->user = $user;
$this->request = $request;
}
This causes the following error.
PHP Fatal error: Uncaught TypeError: Argument 1 passed to ViewTest::__construct() must be an instance of Illuminate\Http\Request, none given, called in phar
It sounds like that __construct() must be an instance of Request, but I don't understand what it actually means, since this code works in, let's say controller classes.
I've searched Laravel's documentation, but I'm not really sure if it's possible to do dependency injection for testing.
I'd appreciate if you would give any advice.

Yii2: Web service Request and Response Logger

I am trying to create a Request and Response Logger for a web service created in Yii 2.0.5 for debugging purpose.
Basically my motive is to track all the request, request data, response data and for this purpose I am using Yii Events. So far I have coded something like this:
UserController
use yii\rest\ActiveController;
use yii\base\Event;
use yii\web\Response;
Event::on(ActiveController::className(), ActiveController::EVENT_AFTER_ACTION, ['app\models\LogHandler', 'saveRequest'], ['request' => Yii::$app->request->getRawBody(), 'response' => Yii::$app->response->content]);
class UserController extends ActiveController
{
public $modelClass = 'app\models\User';
public function behaviors()
{
$behaviors = parent::behaviors();
$behaviors['contentNegotiator']['formats']['text/html'] = Response::FORMAT_XML;
return $behaviors;
}
// POST demo
public function actionDemo()
{
$data = array('status' => 200, 'message' => 'Success');
return $data;
}
}
In the above code if you have noticed then I have used the Class level Event handler (doc). Here I am trying to capture the Controllers EVENT_AFTER_ACTION event and passing the Request & Response object to my LogHandler's static method. However in my handler I am able to get the request's raw body but I am not able to get the response data which the actionDemo() is returning.
LogHandler
namespace app\models;
use yii\db\ActiveRecord;
class LogHandler extends ActiveRecord
{
public static function tableName()
{
return 'request_log';
}
public static function saveRequest($event)
{
// self::load($event);
// self::save();
var_dump($event);
}
}
How do I get the response data as well...
According to the Yii guide here, the best way to create a logger component is to override the class yii\log\Target. Then to send the logs, you need to override the abstract method export() of this class
Since this class is accessed after the life cycle of the request, you will have the request and response data Yii::$app->request and Yii::$app->response and you can access them to create whatever message you want. This is an example of the LogHandler class (this can be edited to include whatever details you want about the request and response)
namespace app\models;
use yii\db\ActiveRecord;
class LogHandler extends ActiveRecord {
$requestPath;
$responseBody;
public static function tableName() {
return 'request_log';
}
}
And an example of a logger class is as follows:
namespace app\components\Logs;
use yii\helpers\VarDumper;
use yii\log\Target;
use Yii;
class LoggerComponent extends Target {
public function export() {
$logMessage = new LogHandler();
$logMessage->requestPath = VarDumper::export(Yii::$app->request->absoluteUrl);
$logMessage->responseBody = VarDumper::export(Yii::$app->response->data);
$logMessage->save();
}
}

Categories