30

I am writing test cases and here is a question I have.

So say I am testing a simple function someClass::loadValue($value)

The normal test case is easy, but assume when passing in null or -1 the function call generates a PHP Warning, which is considered a bug.

The question is, how do I write my PHPUnit test case so that it succeeds when the functions handles null/-1 gracefully, and fail when there is a PHP Warning thrown?

7 Answers 7

39

PHPUnit_Util_ErrorHandler::handleError() throws one of several exception types based on the error code:

  • PHPUnit_Framework_Error_Notice for E_NOTICE, E_USER_NOTICE, and E_STRICT
  • PHPUnit_Framework_Error_Warning for E_WARNING and E_USER_WARNING
  • PHPUnit_Framework_Error for all others

You can catch and expect these as you would any other exception.

/**
 * @expectedException PHPUnit_Framework_Error_Warning
 */
function testNegativeNumberTriggersWarning() {
    $fixture = new someClass;
    $fixture->loadValue(-1);
}
Sign up to request clarification or add additional context in comments.

Comments

15
+50

I would create a separate case to test when the notice/warning is expected.

For PHPUnit v6.0+ this is the up to date syntax:

use PHPUnit\Framework\Error\Notice;
use PHPUnit\Framework\Error\Warning;
use PHPUnit\Framework\TestCase;

class YourShinyNoticeTest extends TestCase
{

    public function test_it_emits_a_warning()
    {
        $this->expectException(Warning::class);

        file_get_contents('/nonexistent_file'); // This will emit a PHP Warning, so test passes
    }


    public function test_it_emits_a_notice()
    {
        $this->expectException(Notice::class);

        $now = new \DateTime();
        $now->whatever; // Notice gets emitted here, so the test will pass
    }
}

3 Comments

Note that this will only work for PHP 7.0. In any later versions there are other PHP-specific exceptions.
@sanmai I don't see that problem. The example above works with php 7.2 and PHPUnit 7.0 as well - just tested it myself.
Thanks! Also to test for specific message you need to call both of these: $this->expectException(Warning::class); $this->expectExceptionMessage('the message');
10

What worked for me was modifying my phpunit.xml to have

<phpunit
         convertErrorsToExceptions="true"
         convertNoticesToExceptions="true"
         convertWarningsToExceptions="true"
         strict="true"
         >
</phpunit>

The key was to use strict="true" to get the warnings to result in a failed test.

1 Comment

Note to the reader, all of these attributes fail on modern versions. Tested on PHPUnit 10.2.2.
3

You can also write a phpunit.xml file (on your tests dir) with this:

<phpunit 
convertErrorsToExceptions="true" 
convertNoticesToExceptions="false" 
stopOnFailure="false">
</phpunit>

1 Comment

Note to the reader, all of these attributes fail on modern versions. Tested on PHPUnit 10.2.2.
1

Using Netsilik/BaseTestCase (MIT License) you can test directly for triggered Errors/Warnings, without converting them to Exceptions:

composer require netsilik/base-test-case


Testing for an E_USER_NOTICE:

<?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_whenNoticeTriggered_weCanTestForIt()
    {
        $foo = new Foo();
        $foo->bar();

        self::assertErrorTriggered(E_USER_NOTICE, 'The warning string');
    }
}

Hope this helps someone in the future.

1 Comment

Please notice that an internal attribute is being set which might cause problems or unexpected behavior in later PHP or PHPUnit versions.
1
public function testFooBar(): void
{
    // this is required
    $this->expectWarning();
    // these are optional
    $this->expectWarningMessage('fopen(/tmp/non-existent): Failed to open stream: No such file or directory');
    $this->expectWarningMessageMatches('/No such file or directory/');

    fopen('/tmp/non-existent', 'rb');
}

Comments

-2

Make SomeClass throw an error when input is invalid and tell phpUnit to expect an error.

One method is this:

class ExceptionTest extends PHPUnit_Framework_TestCase
{
    public function testLoadValueWithNull()
    {
        $o = new SomeClass();            
        $this->setExpectedException('InvalidArgumentException');
        $this->assertInstanceOf('InvalidArgumentException', $o::loadValue(null));
    }
}

See documentation for more methods.

3 Comments

This method requires altering the class under test which is probably a good idea for invalid argument values.
This approach isn't of much use for situations where you want your method to log a warning and continue. For example if you're dividing numbers and require an integer output your class might round the result and issue an E_USER_WARNING to indicate that a rounding occurred. If you throw an exception instead then the flow of execution will be interrupted.
Please notice that strings are not parsed for existing classes. If you want to make sure that the referenced class name is valid use the \InvalidArgumentException::class approach instead. If you have to reference it multiple times, it is a good practice to use \InvalidArgumentException; in the header (after <?php` and before class Foo {}),

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.