5

I have code like this:

class ToBeTested
{
  function simpleMethod($param)
  {
    if(0 === $param)
    {
      trigger_error("Param is 0!", E_USER_WARNING);
      return false;
    }

    return true;
  }
}

and test for this code:

class SimpleTest extends PHPUnit_Framework_TestCase
{
   function testSimpleMethod()
   {
     $toBeTestedObject = new ToBeTested();
     $this->assertFalse($toBeTestedObject->simpleMethod(0));
   }
}

I know how to test, if the error is triggered ($this->setExpectedException()), but I don't know how to execute the code after trigger_error() function.

Remember that in PHPUnit E_USER_WARNING is not converted into PHPUnit_Framework_Error_Warning (which can be disabled), but it is converted into PHPUnit_Framework_Error (which can't be disabled).

tonygreen
  • 83
  • 1
  • 5

4 Answers4

11

This is one of those places where you are 'officially' allowed to use the @ operator :)

Make one test to check the return value, another test to check if the warning gets triggered. And by the way, I'd suggest you do test if the warning is triggered.

class SimpleTest extends PHPUnit_Framework_TestCase
{
   function testSimpleMethodReturnValue()
   {
     $toBeTestedObject = new ToBeTested();
     $this->assertFalse(@$toBeTestedObject->simpleMethod(0));
   }

   /**
    * @expectedException PHPUnit_Framework_Error
    */
   function testSimpleMethodEmitsWarning() {
     $toBeTestedObject = new ToBeTested();
     $toBeTestedObject->simpleMethod(0);
   }
}
edorian
  • 38,542
  • 15
  • 125
  • 143
  • Thanks for this answer. It really looks as a good example to use @ character. :) – tonygreen Sep 27 '10 at 16:10
  • Is this still considered to be the proper approach for testing `E_USER_WARNING`? – tereško Dec 02 '12 at 07:14
  • @tereško Yes. It's still the only somewhat sane way I'm aware of doing this. It might make sense to _first_ test the warning and then the error so in case of E_FATALs the error suppression is off. But `xdebug.scream` is also always there :) – edorian Dec 04 '12 at 16:44
  • php.net docs: [Error Control Operators](http://php.net/manual/en/language.operators.errorcontrol.php) PHP supports one error control operator: the at sign (@). When prepended to an expression in PHP, any error messages that might be generated by that expression will be ignored. – here Dec 11 '14 at 07:51
  • As from phpunit 8.5, you can `expectWarning`, instead – I Want Answers Mar 15 '22 at 12:45
3

What you should be using is set_error_handler() (link) and restore_error_handler() which lets you set a function to deal with errors of a given type. It also has the added bonus of giving you a place to test the warning at the same time.

So, something like this:

class SimpleTest extends PHPUnit_Framework_TestCase
{
   function testSimpleMethod()
   {
     set_error_handler(array($this, '_handleWarnedMethod'), E_USER_WARNING);

     $toBeTestedObject = new ToBeTested();
     $this->assertFalse($toBeTestedObject->simpleMethod(0));

     restore_error_handler();
   }

   private function _handleWarnedMethod($errno, $errstr)
   {
      $this->assertEquals(E_USER_WARNING, $errno);
      $this->assertEquals('Param is 0!', $errstr);
   }

}

As always, error suppression isn't the best idea :)

pivotal
  • 736
  • 6
  • 16
2

The answer is that in PHPUnit 3.4.15 there is PHPUnit_Util_ErrorHandler class with handleError method which is executed when any error occurs. For error like E_USER_* this method always throws PHPUnit_Framework_Error, so execution of the rest of code is stopped.

The only way to prevent this is to disable user errors reporting, I think. It could be done like this:


class SimpleTest extends PHPUnit_Framework_TestCase
{
   function testSimpleMethod()
   {
      $toBeTestedObject = new ToBeTested();
// disable user errors reporting $oldReportingLevel = error_reporting(); error_reporting($oldReportingLevel ^ (E_USER_ERROR | E_USER_WARNING | E_USER_NOTICE));
// check the condition $this->assertFalse($toBeTestedObject->simpleMethod(0));
// recover old error reporting level error_reporting($oldReportingLevel); } }
tonygreen
  • 83
  • 1
  • 5
0

Nearly 9 years later, and this question still comes up regularly.

You can use Netsilik/BaseTestCase (MIT License) to get extended functionality to assert errors/warnings are triggered as expected:

composer require netsilik/base-test-case


Testing for a E_WARNING:

<?php
namespace Tests;

class MyTestCase extends \Netsilik\Testing\BaseTestCase
{
    /**
     * {@inheritDoc}
     */
    public function __construct($name = null, array $data = [], $dataName = '')
    {
        parent::__construct($name, $data, $dataName);

        $this->_convertNoticesToExceptions  = false;
        $this->_convertWarningsToExceptions = false;
        $this->_convertErrorsToExceptions   = true;
    }

    public function test_whenWarningTriggered_weCanTestForIt()
    {
        $foo = new Foo();
        $foo->bar();

        self::assertErrorTriggered(E_WARNING, 'The warning string');
    }
}
Jacco
  • 23,534
  • 17
  • 88
  • 105