3

I'm using phpunit and the ecomdev module to unit test Magento. For a particular class the last code that needs to be tested is an exception on attempting to save a model in a try/catch. The only way I can see to get to the exception is alter the database temporarily. So I changed the table name which works, but then I have to name it back. If the test itself fails I'm in an inconsistent state.

I'd really like to get that code tested so the coverage is 100%. Otherwise I'll be wondering why it's 98% until I look at the code and remember the exception isn't tested.

I was trying to close the connection but I need it to log the exception. So I'm wondering if maybe there is something I can temporarily do to the model or resource model to cause the save to raise an exception. Something that would be reset on the next load.

One note - I can't see anyway that manipulating data will cause the exception. Again the only scenario I see causing an exception in production is if the database connection goes away.

Any ideas?

Edit: Sample Code:

public function logStuff($stuff){

    try{
        Mage::getModel('my/stuff')
            ->setData('stuff', $stuff)
            ->save();

    }catch(Exception $e){
        Mage::helper('log/error')->logError(__METHOD__, "Could not save stuff: ". $e->getMessage());
    }
}
2
  • The solution for you is stub appropriate logic that way it will throw an exception. Could you post the code sample you want test? If you'll post it I could say more.
    – Cyprian
    Commented Dec 19, 2012 at 11:22
  • I added the sample code. It's pretty simple. If the save fails it calls another method that logs the exception message to a table. Thanks!
    – gwgeller
    Commented Dec 19, 2012 at 15:45

1 Answer 1

3

To make test the exception catch, you need to replace your model instance with the mocked one. It is very easy to achieve with EcomDev_PHPUnit extension:

$mockedStuff = $this->getModelMock('your/stuff', array('save'));
$mockedStuff->expects($this->once()) // How many times it is invoked
     ->method('save') // Wich method to mock
     ->will($this->returnCallback(function () {
          throw new Exception('Some error text');
     })); // Your exception

$this->replaceByMock('model', 'your/stuff', $mockedStuff);

Also you can evaluate your logging stuff by using mock as well.

$loggerMock = $this->getHelperMock('log/error', array('logError'));
$loggerMock->expects($this->once())
    ->method('logError')
    ->with('className::methodName', 'Could not save stuff: Some error text'); // Check that correct arguments passed

$this->replaceByMock('helper', 'log/error', $loggerMock);

Have fun with unit tests :)

4
  • Great, thanks! I hope to try this out soon. I will update this thread as soon as I can.
    – gwgeller
    Commented Dec 19, 2012 at 17:20
  • Worked like a charm! Thanks for the answer and for developing the EcomDev_PHPUnit extension!
    – gwgeller
    Commented Dec 19, 2012 at 23:03
  • This might be helpful for others so I'm posting it. Yesterday I spent a fair amount of time trying to mock a helper. In the end the issue I was having was in the code I was testing I was using Mage::helper('module') but to get the mock to take over I had to change it to Mage::helper('module/data'). Of course this only is an issue with the default helpers since Magento adds /data for you.
    – gwgeller
    Commented Jan 24, 2013 at 16:20
  • @gwgeller I believe you could get around the need to append /data by placing your unit test in Helper/Data.php and naming it accordingly
    – scrowler
    Commented Dec 28, 2014 at 21:57

Not the answer you're looking for? Browse other questions tagged or ask your own question.