How to stop event faking (toggle event faking) in Laravel unit testing - laravel

I am developing a Laravel application and doing the unit test. Now, I am having a bit of an issue with faking and un-faking Laravel event in the unit test. What I am trying to do is something like this.
public function test_something()
{
Event::fake();
//Do somethng
//Then I want to stop faking event here something like this
Event::stopFaking(); //maybe
}
I think my code is self-explanatory. How can I achieve something like that in Laravel?

https://laravel.com/docs/5.7/mocking
If you only want to fake event listeners for a portion of your test, you may use the fakeFor method:
$order = Event::fakeFor(function () {
$order = factory(Order::class)->create();
Event::assertDispatched(OrderCreated::class);
return $order;
});
// Events are dispatched as normal and observers will run ...
$order->update([...]);
Everything inside the function() {} will have faked events. Everything outside will function normally.

The Event::fake function is defined in Illuminate\Support\Facades\Event.
We can see that there is a fakeFor method that only fakes it during the execution of a callback method, then restores the original behavior. You can use it like this:
public function test_something()
{
Event::fakeFor(function () {
//Do somethng
});
}
As a Laravel developer it is often useful to read the source code, there are lots of nice bits and pieces in this framework that are not documented!

Related

How to avoid user take long time to wait while controller is processing

let's me explain example
User click button
run function trigger in Controller
User wait 30sec because MyModel::doSomeThing take long time to process
MyModel::doSomeThing do many thing. I don't want user to wait for it.
Is it possible to run MyModel::doSomeThing by don't care about result and return to user immediately?
function trigger(Request $request){
$id= $request->get('id');
MyModel::doSomeThing($id); // this one take 30 sec.
return response()->json([], 200);
}
If the result of doSomeThing() method isn't necessary for your response & can be done in background, I suggest using Events and Listeners, which will use queues to run in the background, and the user won't need to wait for this procces to finish. The process is fairly simple. Create event and it's listened with these two commands:
php artisan make:event YourEvent
php artisan make:listener YourListener --event=YourEvent
After that, register your event and listener in the App\Providers\EventServiceProvider, under the $listen array:
protected $listen = [
YourEvent::class => [
YourListener::class,
],
];
Now, when you have that sorted out, you need to build your event instance. Inside your newly created method, in the construct method, add this:
public $yourModel;
public function __construct(YourModel $yourModel)
{
$this->yourModel = $yourModel;
}
After you created your model, time to edit your listener, which will hanlde all the logic ghat you need. Inside this handle method, you will have the access to $yourModel instance that we defined in our event:
public function handle(YourEvent $event)
{
// Access your model using $event->yourModel...
YourModel::doSomeThing($event->yourModel);
}
The only thing left do to is to make your listener queueable. You can do this by adding implements ShouldQueue your listened definition:
class YourListener implements ShouldQueue
{
//
}
Now when we have everything setup, you can change your controller code to call this newly created event, and let the queue handle all the logic:
function trigger(Request $request){
$id= $request->get('id');
YourEvent::dispatch($id); //Calling event which will handle all the logic
return response()->json([], 200);
}
And that should be it. I haven't tested this code, so if you encounter any problems, let me know.

Laravel 5.6 one event and many optional listeners

in Laravel 5.6
I have an event named DocumentSend,
And i have many Listeners Like (SendEmail, SendNotification, SendSMS),
listeners are optional (depends on document type and defined by user),
now the question is:
How can i call for example DocumentSend event with just SendSMS listener or DocumentSend with all the listeners?
I hope you get my mean,and tell me the best practice for my issue.
Thanks in advance
Well, the simple answers is - you can't. When you fire event all registered listeners will listen to this event and all of them will be launched.
However nothing stops you to prevent running code from listener.
For example you can fire event like this:
event(new DocumentSend($document, true, false, false));
and define constructor of DocumentSend like this:
public function __construct($document, $sendEmail, $sendNotification, $sendSms)
{
$this->document = $document;
$this->sendEmail = $sendEmail;
$this->sendNotification = $sendNotification;
$this->sendSms = $sendSms;
}
and now in each listener you can just verify correct variable, so for example in SendEmail listener in handle you can do it like this:
public function handle(DocumentSend $event)
{
if (!$event->sendSms) {
return;
}
// here your code for sending
}
similar you can do for other listeners.
Of course this is just example - you don't have to use 4 variables. You can set some properties to $document only to mark how it should be sent.

How do you delete all models in an Alloy Collection

From within a controller function, how do you delete all the models in an Alloy Collection. The collection is using properties sync adapter. I think the backbone reset method is the way to go but I could not make it work.
The quickest way for me was to run destroy() on every model. To do this quickly you can use underscore (build in) like this:
_.invoke(Alloy.Collections.library.toArray(), 'destroy');
or even extend the model.js
extendCollection: function(Collection) {
_.extend(Collection.prototype, {
// extended functions and properties go here
dump: function() {
// get all models
return this.models;
},
clear: function() {
// remove/destroy all models
_.invoke(this.toArray(), 'destroy');
}
});
return Collection;
}
and run Alloy.Collections.library.clear();
Pro Tip: you can always search for things like delete all models in backbone and use most of the results right away since it is using backbone in the background.

Laravel 4 how to listen to a model event?

I want to have an event listener binding with a model event updating.
For instance, after a post is updated, there's an alert notifying the updated post title, how to write an event listener to have the notifying (with the post title value passing to the listener?
This post:
http://driesvints.com/blog/using-laravel-4-model-events/
Shows you how to set up event listeners using the "boot()" static function inside the model:
class Post extends eloquent {
public static function boot()
{
parent::boot();
static::creating(function($post)
{
$post->created_by = Auth::user()->id;
$post->updated_by = Auth::user()->id;
});
static::updating(function($post)
{
$post->updated_by = Auth::user()->id;
});
}
}
The list of events that #phill-sparks shared in his answer can be applied to individual modules.
The documentation briefly mentions Model Events. They've all got a helper function on the model so you don't need to know how they're constructed.
Eloquent models fire several events, allowing you to hook into various points in the model's lifecycle using the following methods: creating, created, updating, updated, saving, saved, deleting, deleted. If false is returned from the creating, updating, saving or deleting events, the action will be cancelled.
Project::creating(function($project) { }); // *
Project::created(function($project) { });
Project::updating(function($project) { }); // *
Project::updated(function($project) { });
Project::saving(function($project) { }); // *
Project::saved(function($project) { });
Project::deleting(function($project) { }); // *
Project::deleted(function($project) { });
If you return false from the functions marked * then they will cancel the operation.
For more detail, you can look through Illuminate/Database/Eloquent/Model and you will find all the events in there, look for uses of static::registerModelEvent and $this->fireModelEvent.
Events on Eloquent models are structured as eloquent.{$event}: {$class} and pass the model instance as a parameter.
I got stuck on this because I assumed subscribing to default model events like Event:listen('user.created',function($user) would have worked (as I said in a comment). So far I've seen these options work in the example of the default model user created event:
//This will work in general, but not in the start.php file
User::created(function($user)....
//this will work in the start.php file
Event::listen('eloquent.created: User', function($user)....
Event::listen('eloquent.created: ModelName', function(ModelName $model) {
//...
})

Are cake events handled asynchronously?

At the moment I don't have any queuing functionality in my Cakephp aplication. I will need that in the near future. An upload will result in a batchjob that uses external API with usage limitations, so it would be best if it was handeled in a seperate threat with a queue.
I don't have any experience with this, so I'm going to try a different, but easier, example.
User actions result in e-mails being send. At the moment, the loading of the page is delayed by the (rather long) time it takes the server to send the e-mail. I'd like to use the Event system to fix this. (I am aware I can also do this using this the afterRender function, or dispatch it to a shellTask, but that way I don't learn anything)
From the example page:http://book.cakephp.org/2.0/en/core-libraries/events.html
I've found this example:
// Cart/Model/Order.php
App::uses('CakeEvent', 'Event');
class Order extends AppModel {
public function place($order) {
if ($this->save($order)) {
$this->Cart->remove($order);
$this->getEventManager()->dispatch(new CakeEvent('Model.Order.afterPlace', $this, array(
'order' => $order
)));
return true;
}
return false;
}
}
Let's say the function was called by a controller action:
public function place_order() {
$result = $this->Order->place($this->request->data);
$this->set('result', $result);
}
Now my question... Will the corresponding view be rendered after all the dispatched events completes? or will the Model function just trigger the event and then forget about it?
The last option seems more logical to me (which also resembles the mentioned jQuery functionality in the article)
The problem is that If this were true, I don't understand the later example:
In the example about using results:
// Using the event result
public function place($order) {
$event = new CakeEvent('Model.Order.beforePlace', $this, array('order' => $order));
$this->getEventManager()->dispatch($event);
if (!empty($event->result['order'])) {
$order = $event->result['order'];
}
if ($this->Order->save($order)) {
// ...
}
// ...
}
if the event was just triggered (and then forgot about) there is no way you can asume it has modified the passed event object on the next line of code!
I would like to use cake as much as possible, but I'm not sure if I can get my desired background behavior without shellTasks and external queue. Any tips about these Cake Events?
Cake Events are triggered synchronously. When an event is triggered, all available listeners are called, before proceeding with other instructions.
You can imagine it on your second example as:
public function place($order) {
$event = new CakeEvent('Model.Order.beforePlace', $this, array('order' => $order));
$this->getEventManager()->dispatch($event); // -> all listeners are called at this point
// ... here you can assume your $event was modified
if (!empty($event->result['order'])) {
$order = $event->result['order'];
}
if ($this->Order->save($order)) {
// ...
}
// ...
}

Resources