Disabling event observers programmatically in Magento
I started using unit testing on a project and it took a while to figure out why my tests were not running. Every time I ran phpunit it only ran the first test and then nothing happened:
PHPUnit 4.6.6 by Sebastian Bergmann and contributors. Configuration read from [...] F
No matter what the assertion, the outcome was the same, the script was ending after the first test. Nothing in the logs, nothing in the system logs, total darkness. I installed the EcomDev_PHPUnit module several times (different forks and branches), same result. After intense debugging I found the problem to be with an event listening to controller_front_init_before
, something that was checking the base url again and performing some redirects. So I had to somehow prevent that code from running in test mode.
Disabling events in magento is simple. Just add <type>disabled</type>
to any observer and that’s it. But what if you need to do it at run time? What if under certain conditions certain events need to be unobserved? Turns out, this is not exactly easy – or at least not as easy as I’d expect, nothing like Mage::app()->unobserve('event', 'observer_name')
.
Events are being dispatched by the Mage_Core_Model_App class, but the list with all events is kept in Mage_Core_Model_Config. So, the easiest way is to just set (or add) a certain node on the configuration xml:
Mage::app()->getConfig()->getEventConfig('global', 'controller_front_init_before'); $path = 'global/events/controller_front_init_before/observers/fix_frontcontroller/type'; Mage::app()->getConfig()->setNode($path, 'disabled');
That works just fine if you’re going through the regular flow and the config has been initialized (ie Mage::app()->getConfig()->init()). If you run this too early and the config has not been initialized you set this node for nothing, it will just get erased when the config xml is generated. When running phpunit with EcomDev_PHPUnit, things get a bit more complicated, since objects need to be mocked, espacially the app and config objects and config init is ran several times.
It turns out that listening to phpunit_suite_start_after
does the trick. Put the code above in a listener and you should be ready to test.
Another approach involves the use of reflection class, but I wouldn’t use that unless there was no other way. Here’s the approach, just for the sake of it:
$app = Mage::app(); $class = new ReflectionClass(get_class($app)); $property = $class->getProperty('_events'); $property->setAccessible(true); $events = $property->getValue($app); $events['global']['controller_front_init_before']['observers']['fix_frontcontroller']['type'] = 'disabled'; $property->setValue($app, $events);
Again, same assumption – config has been initialized.
And the last approach, and certainly much easier whenever possible: just hack the code and add a flag. Something along the lines if !registry(flag) then run this code;
. This is a really simple solution, but it doesn’t work with code that cannot (should not) be changed (mainly because it would get overwritten on upgrades), hence the approach above.
Hope this helps, it took me good hours to figure everything out.
Inspired from the answers here