guh.me - gustavo's personal blog

How to assert that an exception is not thrown on PHPUnit

βΈ»

I’m refactoring a legacy PHP codebase, and that includes writing new tests. While doing that, I found a class method that accepts only a predetermined set of values, and refuses any other value by throwing an exception. Below is a simplified representation of this class:

<?php

class MyClass
{
    protected $value;

    public function setValue($value)
    {
        if (in_array($value, ['foo', 'bar'])) {
            $this->value = $value;
            return $this;
        }

        throw new RuntimeException("{$value} is not a valid value.");
    }
}

Promptly, I wrote a test to check if the method was filtering out invalid input values:

<?php

use PHPUnit\Framework\TestCase;

class MyTestCase extends TestCase
{
    protected $instance;

    public function setUp()
    {
        $this->instance = new MyClass();
    }

    public function testSetValueRefusesBadInput()
    {
        $this->expectException(RuntimeException::class);
        $this->instance->setValue('baz');
    }
}

Easy peasy.

But what if the method is not handling valid input values correctly? Since the method accepts only a small set of values, we could test all of them. However, PHPUnit 6 doesn’t have a doNotExpectException assertion, nor does it allow a test without an assertion.

We can easily overcome these limitations by adding an assertion to the end of the test ($this->assertTrue(true)) or by increasing the assertion count ($this->addToAssertionCount(1)). If the MyClass::setValue method implementation is incorrect, an exception will be thrown, otherwise, the assertion will be accounted for and PHPUnit will not “complain” about the test.

<?php

use PHPUnit\Framework\TestCase;

class MyTestCase extends TestCase
{
    protected $instance;

    public function setUp()
    {
        $this->instance = new MyClass();
    }

    public function testSomethingWorks()
    {
        $this->instance->setValue('bar');
        $this->addToAssertionCount(1);
    }

    public function testAnotherThingWorks()
    {
        $this->instance->setValue('foo');
        $this->assertTrue(true);
    }
}