Laravel: Test not Using Mocked Method - php

I want getNumber to return 200 in the test case, but when I run the test and inspect the result with $response->dump() I can see that the mock is not overriding the method as it is showing array:1 [0 => 100]. What am I missing?
Controller:
class ServiceController extends Controller
{
public function fetch(Request $request)
{
$service = new Service();
return [$service->getNumber()];
}
}
Model:
class Service extends Model
{
public function getNumber(): int
{
return 100;
}
}
Test:
class ControllerTest extends TestCase
{
public function testFetchGetsNumber()
{
$mock = $this->partialMock(Service::class, function (MockInterface $mock) {
$mock->shouldReceive('getNumber')->andReturn(200);
});
$response = $this->post('/api/controller');
$response->dump(); // returns 100 but should return 200...
}
}
I am using Laravel 8 and referencing this documentation: https://laravel.com/docs/8.x/mocking#mocking-objects

In Laravel you have to use the container to mock classes.
$service = resolve(Service::class);
In constructors or handle methods, if you inject your classes there it will automatically use the container. If put into constructors or classes, remember the resolution of the class with the constructor, have to use the container.
class Job
{
public function handle(Service $service)
{
// will use the mocked service.
}
}

Related

Laravel 7: Service provider bind a class with a constructor parameter model

I've created a Service Provider with a class that has a model passed into the constructor.
The model needs to be a specific record based off the $id taking from the URL eg /path/{$id}
How can I use the requested model in the Service Provider?
An option is to pass the model into the execute method but for now I'll need to pass it into the construct.
MyController
class MyController {
public function show(MyClass $myClass, $id)
{
$model = MyModel::find($id);
return $myClass->execute();
}
}
MyClass
class MyClass
{
$private $myModel;
public function __construct(MyModel $myModel)
{
$this->myModel = $myModel;
}
public function execute()
{
//do something fun with $this->myModel
return $theFunStuff;
}
}
MyServiceProvider
public function register()
{
$this->app->singleton(MyClass::class, function ($app) {
return new MyClass(/* How can I use $myModel? */);
});
}
I don't see any value / reason to use a singleton here.
The service provider registers the singleton before your route is resolved, so there is no way to pass the $model from the controller into the register method. I would remove the service provider and do the following:
From the docs:
If some of your class' dependencies are not resolvable via the
container, you may inject them by passing them as an associative array
into the makeWith method:
$api = $this->app->makeWith('HelpSpot\API', ['id' => 1]);
So in your case something like this:
public function show($id)
{
return app()->makeWith(MyClass::class, ['myModel' => MyModel::find($id)])->execute();
}
Or shorter with the help of route model binding:
public function show(MyModel $myModel)
{
return app()->makeWith(MyClass::class, compact('myModel'))->execute();
}
Note that the argument names passed to makeWith have to match the parameter names in the class constructor.

PHP: Mockery Mock variable $user = Auth::user()

So, I am trying to mock a service method.
In my service file:
/**
* Return all Api Keys for current user.
*
* #return Collection
*/
public function getApiKeys(): Collection
{
$user = Auth::user();
return ApiKey::where('org_id', $user->organizationId)->get();
}
How do I mock this?
<?php
namespace App\Services;
use PHPUnit\Framework\TestCase;
use Mockery as m;
class ApiKeysServiceTest extends TestCase
{
public function setUp()
{
parent::setUp();
/* Mock Dependencies */
}
public function tearDown()
{
m::close();
}
public function testGetApiKeys()
{
/* How to test? $user = Auth::user() */
$apiKeysService->getApiKeys();
}
}
In my TestCase class I have:
public function loginWithFakeUser()
{
$user = new GenericUser([
'id' => 1,
'organizationId' => '1234'
]);
$this->be($user);
}
What I want to do is test this method. Maybe this involves restructuring my code so that $user = Auth::user() is not called in the method. If this is the case, any thoughts as to where it should go?
Thanks for your feedback.
In your testGetApiKeys method you're not setting up the world. Make a mock user (using a factory as suggested in the comments factory('App\User')->create()), then setup an apiKey again using the factory, then call the method and assert it's what you've setup. An example with your code
public function loginWithFakeUser()
{
$user = factory('App\User')->create();
$this->be($user);
}
public function testApiSomething()
{
$this->loginWithFakeUser();
// do something to invoke the api...
// assert results
}
A good blueprint for the test structure is:
Given we have something (setup all the needed components)
If the user does some action (visits a page or whatever)
Then ensure the result of the action is what you expect (for example the status is 200)

Laravel ::__construct() must implement interface

I'm using Laravel 5.4 And I'm trying to inject a $order class into a trait that's going to implemented by a model. Like this:
class Forum extends Model
{
use Orderable;
The constructor of the trait looks like this:
trait Orderable
{
public $orderInfo;
public function __construct(OrderInterface $orderInfo)
{
$this->orderInfo = $orderInfo;
}
My service provider looks like this:
public function register()
{
$this->app->bind(OrderInterface::class, function () {
return new Order(new OrderRepository());
});
$this->app->bind(OrderRepositoryInterface::class, function () {
return new OrderRepository();
});
}
The constructor of my Order class looks like this:
public function __construct(OrderRepositoryInterface $orderInfo)
{
$this->orderInfo = $orderInfo;
}
But I receive the error:
Type error: Argument 1 passed to App\Forum::__construct() must implement interface Project\name\OrderInterface, array given, called in /home/vagrant/Code/Package/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Model.php on line 268
The OrderRepository class is implementing the OrderRepositoryInterface. The Order class is implementing the OrderInterface interface.
App\Forum is the model that uses the Orderable trait.
What could I be doing wrong here?
You are extending Model. This class already has a __construct that you need to use. This __construct expects array $attributes = [] as the first argument.
So in your __construct you also need to have this as the first argument and pass this to the parent class:
public function __construct(array $attributes = [], OrderRepositoryInterface $orderInfo)
{
$this->orderInfo = $orderInfo;
parent::__construct($attributes);
}
However you can work around using __construct in laravel using boot.
For example in a Model:
class Forum extends Model
{
protected static function boot()
{
parent::boot();
// Do something
}
}
Or in a Trait:
trait Orderable
{
public static function bootOrderableTrait()
{
static::created(function($item){
// Do something
});
}
}
In PHP it's not possible to have multiple constructors. If you will look to Model:
public function __construct(array $attributes = [])
it expect array. That's why I assume that somewhere in Model array passed to constructor instead of 'OrderInterface'.

Laravel: Avoid to create instance of a model with a constructor in the controller

I'm following a course for Laravel 4 and the teacher did a code refactoring and introduced a magic method constructor in the controller
class UtentiController extends BaseController {
protected $utente;
public function __construct(Utenti $obj) {
$this->utente = $obj;
}
public function index() {
$utenti = $this->utente->all();
return View::make('utenti.index', ["utenti" => $utenti]);
}
public function show($username) {
$utenti = $this->utente->whereusername($username)->first(); //select * from utenti where username = *;
return View::make('utenti.singolo', ["utenti" => $utenti]);
}
public function create() {
return View::make('utenti.create');
}
public function store() {
if (! $this->utente->Valido( $input = Input::all() ) ) {
return Redirect::back()->withInput()->withErrors($this->utente->messaggio);
}
$this->utente->save();
return Redirect::route('utenti.index');
}
}
Thanks to this code I don't have to create a new instance of the Utenti model every time:
protected $utente;
public function __construct(Utenti $obj) {
$this->utente = $obj;
}
Now I can access the database with this simple approach:
$this->utente->all();
Whereas before, I had to do this:
$utente = new Utente;
$utente::all();
Does this type of technique have a name? (is it a pattern?).
My understanding is that every time the controller is invoked it automatically generates an instance of the User class (model) and applies an alias (reference) attribute $utente
Is that correct?
Also, here is the code for the Utenti model:
class Utenti extends Eloquent {
public static $regole = [
"utente" => "required",
"password" => "required"
];
public $messaggio;
public $timestamps = false;
protected $fillable = ['username','password'];
protected $table = "utenti";
public function Valido($data) {
$validazione = Validator::make($data,static::$regole);
if ($validazione->passes()) return true;
$this->messaggio = $validazione->messages();
return false;
}
}
This is called dependency injection or short DI. When creating a new instance of the Controller, Laravel checks the constructor for type hinted parameters (The ones that have a type defined like __construct(Utenti $obj){) If your controller has any of these Laravel tries to create an instance of the class and injects it into the constructor.
The reason why this is done is that it's becoming very clear what the dependencies of a class (in this case your controller) are. It gets especially interesting if you type hint an Interface instead of a concrete class. You then have to tell Laravel with a binding which implementation of the interface it should inject but you can also easily swap an implementation or mock it for unit testing.
Here are a few links where you can get more information:
Laravel docs IoC container
Method dependency injection in Laravel 5
StackOverflow - What is Inversion of Control?

ZF2 : Call a service from an external class?

In my Zend Framework 2 project, I have an external lib and I want to save my information in the base with the model.
....
....
....
EDITED MESSAGE :
I explain again my need: In my controllers, I make insertions and deletions in the database and I want to log all actions in a "t_log" table . To do it, I have thought to create an external class.
My question is: How I can call my models method from my external class ?
namespace Mynamespace;
use Firewall\Model\Logs;
use Firewall\Model\LogsTable;
class StockLog
{
public function addLog()
{
$log = $this->getServiceLocator()->get('Firewall\Model\LogTable');
$log->save('user added');
die('OK');
}
}
My model :
namespace Firewall\Model;
use Zend\Db\TableGateway\TableGateway;
use Zend\Db\Sql\Select;
class UserGroupTable
{
protected $tableGateway;
public function __construct(TableGateway $tableGateway)
{
$this->tableGateway = $tableGateway;
}
public function save()
{
// How I Can call this method from the StockLog method ?
}
}
Thanks you !
getServiceLocator is a function of \Zend\Mvc\Controller\AbstractActionController so it is supposed to be used in your controllers.
I dont know what your StockLog class is, but it is not extending any other class, so i guess it has not that function and your error is one step before, in the call to getSErviceLocator that is not defined, so its not returning an object.
Probably you can inject the service locator with something like
class StockLog
{
private $serviceLocator= null;
public function setServiceLocator(ServiceLocatorInterface $serviceLocator)
{
$this->serviceLocator = $serviceLocator;
}
public function add()
{
# Do you know how I can call the service ??
$User = $this->serviceLocator->get('Firewall\Model\UserTable');
}
}
and then, when you create your StockLog object, in your controller, you inject the servicelocator
public class yourController extends AbstractActionController {
public function yourAction(){
$mStockLog = new StockLog ();
$mStockLog->setServiceLocator($this->getServiceLocator());
/...
}
}
Also, if you only need the 'Firewall\Model\UserTable' service, you should inject just that, instead of the serviceLocator.
At any rate you should minimice the knowledge of your model classes about the rest of the system, hving always in mind the dependency inversion principle, to get a better decoupling
UPDATE
inject the log table
namespace Mynamespace;
use Firewall\Model\Logs; use Firewall\Model\LogsTable;
class StockLog {
private $logTable= null;
public function setLogTable($logTable)
{
$this->logTable= $logTable;
}
public function addLog()
{
$this->logTable->save('user added');
die('OK');
}
}
and then, when you create your StockLog (in your controller, or wherever you do it, before you use it) you inject the logtable object
$mStockLog = new StockLog ();
$mStockLog->setLogTable($this->getServiceLocator()->get('Firewall\Model\LogTable'));
Of course, Im suposing that you configured correctly your Firewall\Model\LogTable class to be retrieved by means of the service manager, in getServiceConfig() in your Module.php
public function getServiceConfig() {
return array (
'factories' => array (
'Firewall\Model\LogTable' => function ($sm) {
$logTable = //create it as you use to
return $logTable;
}
)
}

Categories