Mockery: Method does not exist on mock object - laravel

So I have an object which I am mocking and it has methods that are used for sending emails. I have gone from a static class over to a concrete class because I was having issues testing the static class with Mockery.
However I am now finding that when an eloquent model is saved it throws an event which fires off a listener. This listener is what is responsible for kicking off the call to the mock objects method.
I have found that when I do something like the following it works.
$model = factory(MyClass::class)->make();
$model->property = 'value';
$model->save()
$this->mailer->shouldHaveReceived('methodName')->with($arg1, $arg2, $arg3);
It will fail every time complaining that the method does not exist on the mock object, the worst part is if I go into my listener where the mock object method is called and I do something like this
echo '<pre>'.print_r(get_class_methods($this->mailer), true).'</pre>'; exit;
it shows that the method is in-fact in the array of methods returned from get_class_methods.
Now here is the kicker. Everything works perfectly and the test passes without any errors if I do the following:
$this->mailer->shouldReceive('methodName');
$model = factory(MyClass::class)->make();
$model->property = 'value';
$model->save()
Now I have been reading the Mockery Gotchas and it would appear that if my class was calling a method that truly didn't exist on the object via ___call then this error would be expected.
However my mailer class is NOT extending any other class that would have a __call method nor does it have that method defined on the class.
However as you may or may not know Eloquent Models do in-fact use the magic ___call method. However I am NOT mocking the model with Mockery, I am using factory models in Laravel but I am also not checking the model object for the method call, I am checking my concrete mailer class for the method call.
Anyone know why I might be getting this behavior from Mockery?
I am creating my mock object in Laravel 5.2 with the following code
public function mock($class)
{
$mock = Mockery::mock($class);
$this->app->instance($class, $mock);
return $mock;
}
$this->mailer = $this->mock('Namespace\Classname');

How are you creating $this->mailer? In order to be able to use the spy functionality ->shouldHaveReceived, you need to have invoked Mockery::spy($className) or called the shouldIgnoreMissing method after creating the test double with Mockery::mock($className).

Related

Laravel-Class Method with Reflected Parameters

I usually use parameters like this:
public function test($parameter)
{
echo 'Parameter value: ' . $parameter;
}
While looking at laravel service container I see this code.
public function __construct(UserRepository $users)
{
$this->users = $users;
}
According to the documentation it uses reflection.But i dont understand.
I dont know how the parameter UserRepository $users works. Is that an alias or something?
This is called type-hinting and is used to inject dependencies in a constructor or to validate the right type of argument is passed to a function. The injection simply means that if the class is called with the make method, Laravel will automatically provide an instance of the class required by your constructor.
For example if you have a function public function something(string $something) it would throw an error if any other type than a String is passed to this function, making sure the right data is used.
From the laravel documentation:
Alternatively, and importantly, you may "type-hint" the dependency in the constructor of a class that is resolved by the container, including controllers, event listeners, queue jobs, middleware, and more. In practice, this is how most of your objects should be resolved by the container.
For example, you may type-hint a repository defined by your application in a controller's constructor. The repository will automatically be resolved and injected into the class:
Laravel has a great service container and it makes all dependency injections, so you don't need to pass a class a parameter, laravel do it for you.
without container you have to pass this parameter
class A {
public $foo;
public function __construct (Foo $foo){
$this->foo
}
$classA = new A((new Foo))
When laravel encounter with these classes, it resolves them.
Also you can define manually these classes using singleton() or bind() methods
$this->app->singleton('FooBar', function($app)
{
return new FooBar($app['SomethingElse']);
});
Or you may use interfaces. You can bind implemented class for to the interface and laravel when encounter with that interfance, it will resolve as you wish
$this->app->bind('App\ICacheManager', 'App\RedisManager');
public $redis;
public function __contruct(ICacheManager $redis){
$this->redis = $redis;
}
for more further check out laravel service container

Mock out function call to another function in same class

I have function that calls out to another function in the same class. I'm trying to test if that method is called in my unit tests however can't find a way to mock it.
Here is an example of the method call
public function follow($user_id, $followsfeed, $target_user_id)
{
$news_feeds = $this->getNewsFeeds($user_id);
}
I want to mock the method getNewsFeed and assert it is being called when I execute follow.
What is the best way to achieve this. as I can't workout how to create an internal mocked object.
You can use a partial mocks, as example on your test class, you can do:
public function test_follow()
{
$mock = Mockery::mock('App\Services\SomeService')->makePartial();
$mock->shouldReceive('getNewsFeeds')
->once()
->with("id1","id2")
->andReturn($someNews);
$mock-> follow("id1","id2");
}
You can also use the PHPUnit test-doubles
Hope this help

Sending request to a post controller function from another controller function

I have a controller like this.
public function barcode_approve(Request $request)
{
$barcodes = $request->input('barcode_values');
$upload_ids = $request->input('upload_id');
....
}
It is defined as post in route without any problem.
Route::post('my/url','BarcodeScanController#barcode_approve');
I would like to use barcode_approve by sending request from controller like
public function push_approve(){
$request = ['barcode_values' => '23ssdwe','upload_id'=>234234];
$this->barcode_approve($request);
.....
}
But $reqeust->input doesn't give the value when I send it through controller function (push_approve)
How can I send values from another controller function as input in $request?
Your barcode_approve expects a Request object, not an array. While you could instantiate a new Request object and potentially inject your fake inputs, I would consider that a bad practice.
Whenever you find yourself trying to call a controller method from another controller method, it usually means you have logic that can be extracted, either to a model, a trait, or in the case of the same controller, a separate protected function.
The purpose of a controller is to be the transport method, you shouldn't have much business logic in it, extract logic to the models when you can.
Required argument in barcode_approve() method must be instance of Request, not just array even if u call it $request.
So if you realy need your code to work, you must create Request instance at your push_approve method()
$request = new Request(['barcode_values' => '23ssdwe','upload_id'=>234234]);
But better way will be to route Request to push_approve() initially
extends your controller with the controller you want to used
then call the function this->barcode_approve($request)

ZF2: Attach event to another Controller's Action and get Service Locator

I am new to Zend Framework 2. I have two modules 'A' and 'B'. I trigger an event in LoginController's LoginAction of "A" module. I want to attach LoginController's LoginAction or LoginController's testMe() method.
In "A" module's LoginController's LoginAction, I have written
$this->getEventManager()->trigger('checkme.post', null, array('user_id' => $userData->usydstudentid));
In Module.php of "B" module, in on Bootstrap method, I have given
$loginController = new B\Controller\LoginController();
$sharedEventManager->attach('A\Controller\LoginController', 'checkme.post', array($loginController, 'LoginAction'), 100);
In LoginController's LoginAction of "B" module, I can access data but I can not access service manager in order to get module' config. When I try to use
$this->getServiceLocator()->get('Config');
I get error
Call to a member function get() on a non-object
In other words, I want to trigger an event from one controller's method and attach to another controller's method. After listening, getting data from that event, I want to get module's config. Please help in this regard.
First of all, you shouldn't use events and controllers this way. Your controller from B isn't a controller, but you should put that one rather in a service layer.
Then, the service locator must be injected. If you do $controller = new SomeController this service locator is not injected. Thus, fetching any object from it will fail. What you should do is using the controller loader to get the controller.
So instead of this:
$loginController = new B\Controller\LoginController();
$sharedEventManager->attach('A\Controller\LoginController',
'checkme.post',
array($loginController, 'LoginAction'),
100);
You should write this:
// $sl is instance of Service Locator
// E.g. $sl = $e->getApplication()->getServiceManager();
// Where $e is the event from the onBootstrap() method
$loader = $sl->get('ControllerLoader');
$loginController = $loader->get('B\Controller\LoginController');
$sharedEventManager->attach('A\Controller\LoginController',
'checkme.post',
array($loginController, 'LoginAction'),
100);
But as said, triggering an action in a controller this way with events isn't realy a good way to do it. You better dispatch it with for example the controller plugin Forward or (as I said it before), remove the logic from the controller's LoginAction and locate it in a service class or something.

With Codeigniter, what's the difference if I wanted to set a method to private?

With callback functions that are within the same class, I can't set the following.
private function check_valid_image
{
...
}
I can get it to work if I do the following.
function _check_valid_image
{
...
}
By placing an underscore in front of the method name, is that the same as placing the word private in front?
It's a convention used with the form validation class for callbacks. It also makes that method not callable via the URL segments.
That being said, it is not the equivalent of making a method private, which has implications in how code can be run outside of the class.

Resources