Check out PHPSpec with MageSpec if you want something not based on PHPUnit, but with some Magento Integration.
Its usable, but not just quite ready for prime time.
For mocking PHPSpec 2 uses Prophecy (which is great), but doesn't like magic getters and setters at all.
For that reason its better to use Mockery instead, which works fine (and I also like it a lot).
Then there also is Behat with BehatMage for BDD with Magento. Worth a look, but it fails in for example if you want to test different store views on different domains.
There also is an extension to BehatMage called MageBehat (awful naming!), which came out of a hackathon.
It provides a lot of Magento specific actions to BehatMage, but to be honest, some of the workarounds used are more of a hack then real solutions to the underlying issues.
Still, its usable.
I hope to see both projects - MageSpec and BehatMage - flourish in future.
Given the information you posted, I suspect that the class \Custom\Shipping\Helper\Config
implements (or inherits) public function __call()
from a parent class and that the method getProductTypes()
is not a real method but rather a "magic getter".
Magic methods have to be explicitly specified on generated test doubles, otherwise they can not be stubbed for specific return values or otherwise configured.
Try to create the stub as follows:
$this->configHelperMock = $this->getMockBuilder(\Custom\Shipping\Helper\Config::class)
->setMethods(['getProductTypes'])
->disableOriginalConstructor()
->getMock();
This will only stub that one method on the generated test double class.
If you want the test double to stub all methods, not just the one magic getter, while still specifying additional "magic" methods, you have to specify all methods, for example like so:
$methodsToStub = array_merge(
get_class_methods(\Custom\Shipping\Helper\Config::class),
['getProductTypes']
);
$this->configHelperMock = $this->getMockBuilder(\Custom\Shipping\Helper\Config::class)
->setMethods($methodsToStub)
->disableOriginalConstructor()
->getMock();
That is one of the reasons to favor real methods over magic methods.
I'm not 100% sure this is the reason why you are experiencing this issue since you didn't post the source of \Custom\Shipping\Helper\Config
, but please let me know if this helps.
I can't stop myself from adding the following suggestions, too:
Use the PHP 5.5 ::class
constant instead of string class names, that way your IDE (hopefully PHPStorm) can help you find typos.
E.g. $this->getMockBuilder(\Custom\Shipping\Helper\Config::class)
Whats more, you can also use class imports for readability, so in your case just $this->getMockBuilder(Config::class)
In Unit tests, don't use the ObjectManager at all. Just instantiate your class with new
, since all dependencies are test doubles anyway.
Less magic makes for easier debugging. Especially like in your case where there is only a single dependency.
$this->sourceModel = new \Custom\Shipping\Model\Config\Source\ProductTypes(
$this->configHelperMock
);
Use more descriptive names for the test methods. It pays off a few months later when the under test has already been forgotten and suddenly the test breaks after an upgrade or similar change.
E.g. testToArrayReturnsIdenticalKeysAndValues()
Best Answer
I also like to ensure that the observer is in fact called when doing an appropriate action, e.g. when you dispatch a catalog_product_save_after, then do something like:
This will fail if your method is not called exactly once.