Assert laravel model observer's event fired in PHPUnit - laravel

I'm going to write some tests for my laravel 7 controller methods (endpoints) to be sure that they fire model observer event (for example saved event on Product model)
as you now model observer events do not fire while using mass update and it's possible my teammates use a mass update (by mistake), then any event does not fire and it breaks the remaining processes
So by these tests, I can be sure that all parts that rely on model observer events work currently.
In laravel testing document mentioned that if you want to assert an event has been dispatched use this code (for example):
public function testOrderShipping()
{
Event::fake();
$order = factory(Order::class)->create();
// Assert an event was dispatched
Event::assertDispatched(OrderCreated::class);
}
Is it possible to assert model observer's event has been fired (dispatched) too?

Related

Testing assertDispatched only works when using event helper

Registering service provider:
public function register()
{
$this->app->singleton(CartInterface::class, function ($app) {
return new SessionCart($app['session'], $app['events']);
});
}
Firing events within the service above:
$this->events->fire('cart.added', $item);
Testing the service:
public function it_can_add_an_item()
{
Event::fake();
$this->cartService->add(new Item);
$this->assertEquals(1, $this->cartService->count());
Event::assertDispatched('cart.added');
}
Result:
The expected [cart.added] event was not dispatched.
If instead of using $this->events to fire events I just use the event helper like event('cart.added') I'm back to green.
I'm not dying for using the object oriented approach, but I'm really curious about the reason it doesn't work here, because the event helper seems to be using an instance of the dispatcher right from the container just like I do when registering the service.
Any clue?
This is what happens when your test is executed:
The laravel application is bootstrapped; it binds the non-fake event dispatcher to the service container
You bind the CartInterface to the service containing and pass the currently binded event dispatcher (the non-fake one) as a parameter
After the application has been bootstrapped, you swap the event dispatcher with the fake one, however, your CartInterface has already been resolved (since its a singleton) and does not know that the event dispatcher has been swapped
Therefore, the CartInterface still uses the non-fake event dispatcher and your tests fail if you try to make any event assertions. However, if you use the Event facade inside your CartInterface, the event dispatcher will be resolved when you are actually using it, and this is after the dispatcher has been swapped with the fake one. So in this case, you actually get the event fake you want and your tests pass.

Pimcore Which event is fired when first time product save?

Pimcore How to get event for first time product save using backend.
I have to apply some logic for first time product creation in pimcore.
How to find event name.
The name of the event is 'object.preAdd'.
If the Id of the saved object is 0, it's a newly created one
\Pimcore::getEventManager()->attach("object.preAdd", function (\Zend_EventManager_Event $e) {
// your code goes here
});
It would probably be best if you stick the code above into a custom made plugin to ensure it's executed on every call.
See https://www.pimcore.org/docs/latest/Extending_Pimcore/Event_API_and_Event_Manager.html for more information

Laravel testing without firing cache events

I am using the array cache driver while testing, however I want to disable the Cache Events.
I can do this with
$this->withoutEvents();
I'd rather just stop the one event, however
$this->expectsEvents(Illuminate\Cache\Events\KeyForgotten::class);
will throw an error if the event is not called.
One solution would be a function that on the outside allows an event to fire and hides it but doesn't throw an error if the event doesn't occur.
I think I need to mock the Events Dispatcher like so
$mock = Mockery::mock('Illuminate\Contracts\Events\Dispatcher');
$mock->shouldReceive('fire')->andReturnUsing(function ($called) {
$this->firedEvents[] = $called;
});
$this->app->instance('events', $mock);
return $this;
The question would be how to carry on dispatching the non caught events?

How does a Laravel queued job handle a deleted model as input?

What happens if a Laravel queued job is passed an Eloquent model as input, but the model is deleted before the job gets run in the queue?
For example, I am building an eCommerce site with Laravel 5.2 where a customer can enter addresses and payment methods. A payment method belongs to an address. But if a customer tries to delete an address, rather than cascading down and deleting any payment methods that are associated with it, I soft delete the address by marking it as disabled. That way, the payment method can still be used until the customer updates the billing address associated with it.
However, if the payment method is deleted and it references an address that has been soft-deleted, I want to do some garbage collection and delete the address from the database. This doesn't need to happen synchronously, so I wrote a simple queueable job to accomplish this. The handle method looks like this:
public function handle(PaymentMethodRepository $paymentMethodRepository, AddressRepository $addressRepository)
{
$billingAddress = $paymentMethodRepository->address($this->paymentMethod);
if ( ! $billingAddress->enabled) {
$addressRepository->delete($billingAddress);
}
}
I dispatch this job in the destroy method of the PaymentMethodsController. However, if the payment method passed to the job is deleted from the database before the job gets executed in the queue, will the job fail?
I'm still developing the site so I don't have a server to deploy and test out what happens. I know that the model gets serialized to be put in the queue, but I wonder if an issue would occur when the model is restored to execute the job.
Yes, the job will fail if the "serialized model" is deleted before the job is executed. The model is not really serialized - the job stores the model class and model identifier and fetches the model before execution.
To get around this, you could store the primary key of the model in the job and then when executing the job, check to see if the record exists:
class DeleteAddressJob extends Job implements ShouldQueue
{
private $addressId;
public function __construct(int $addressId)
{
$this->addressId = $addressId;
}
public function handle(AddressRepository $addressRepository)
{
$address = $addressRepository->find($this->addressId);
if (is_null($address)) {
// Address doesn't exist. Complete job...
return;
}
// Delete the address...
}
}

Magento Event On Any Page Load

I am wondering if there is an event that is fired once every time a page is loaded before rendering to html in magento?
This might be useful if you want to do some business logic for semi-static attributes that don't rely on user sessions.
For example I will be using this to deliver the canonical tag to the header of magento.
There are several request-related events which are dispatched for most page-/content-generating requests. Below is a partial list in processing order of some useful ones, and I expect others may comment on this post with some others. Many of these are not suitable for your need (I've noted in bold below where you should begin considering). There are also a few block-instantiation-related events which, although they could be observed for your purpose, are generic to every block and really aren't appropriate.
The first practical singly-fired event is controller_front_init_before. This event is dispatched in Front Controller initialization in response to all dispatched requests. Because it is dispatched before the action controllers are invoked, only global-area observers will be able to observe this event.
Assuming the request is routed from the Front Controller through the routers to an action controller, there are some events which can be observed prior to rendering in preDispatch() - note the generic controller_action_predispatch event handle which could be consumed for all events vs the two dynamic event handles:
Mage::dispatchEvent('controller_action_predispatch', array('controller_action' => $this));
Mage::dispatchEvent('controller_action_predispatch_' . $this->getRequest()->getRouteName(),
array('controller_action' => $this));
Mage::dispatchEvent('controller_action_predispatch_' . $this->getFullActionName(),
array('controller_action' => $this));
How a response is being rendered may affect the events available; the main variations would come from whether or not layout updates are being used to render the response (and how). For example, core_layout_update_updates_get_after could be used to inject a layout update file to the list of configured module layout update files (a rare but potentially useful case). The controller actions are closely coupled with the layout modeling, so there are a few events which could work:
controller_action_layout_load_before
controller_action_layout_generate_xml_before
controller_action_layout_generate_blocks_before and controller_action_layout_generate_blocks_after - the latter of which would be the first applicable to your needs
Assuming that renderLayout() is being used in all actions about which you care, there are two events (one generic and one route-specific) which it dispatches:
Mage::dispatchEvent('controller_action_layout_render_before');
Mage::dispatchEvent('controller_action_layout_render_before_'.$this->getFullActionName());
After all of the routing, dispatching, view configuring, block instantiating, and rendering are done, there is one last-ditch event which is dispatched by the Front Controller before the response is sent: controller_front_send_response_before. This event is not suitable for your need, but it's a nice bookend to the controller_front_init_before event which began this answer.
http://www.nicksays.co.uk/magento-events-cheat-sheet-1-7/ this will help.
app/code/core/Mage/Core/Controller/Varien/Action.php this event
controller_action_layout_load_before
is fired
app/code/core/Mage/Core/Block/Abstract.php event
core_block_abstract_to_html_before
above two events might be of help.
I think you are looking for this event controller_action_layout_render_before_RouteName_ControllerName_ActionName
you can also log all the events which are fired on any page load from Mage class in below function
public static function dispatchEvent($name, array $data = array())
{
Mage::log($name);
Varien_Profiler::start('DISPATCH EVENT:'.$name);
$result = self::app()->dispatchEvent($name, $data);
Varien_Profiler::stop('DISPATCH EVENT:'.$name);
return $result;
}
We can use controller_front_init_routers event using observer. In that observer method,you can get request object as follows.
$request = $observer->getEvent()->getData('front')->getRequest();

Resources