19

PHP 7.4 and PHPUnit 9

Using the PHPUnit homepage example (https://phpunit.de/getting-started/phpunit-9.html):

private function ensureIsValidEmail(string $email): void
{
    if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
        throw new InvalidArgumentException(
            sprintf(
                '"%s" is not a valid email address',
                $email
            )
        );
    }
}

The homepage also shows us how to test the exception is thrown using the expectException() method:

public function testCannotBeCreatedFromInvalidEmailAddress(): void
{
    $this->expectException(InvalidArgumentException::class);

    Email::fromString('invalid');
}

That's great. But what if I want to test the exception is not thrown given valid input ?

Looking at the docs (https://phpunit.readthedocs.io/en/9.3/writing-tests-for-phpunit.html#testing-exceptions) there seems to be no mention of an inverse method to expectException() ?

How should I approach this ?

EDIT TO ADD:

Just to make it perfectly clear, I'm looking to test an Email::fromString('[email protected]'); scenario, i.e. that the exception is not thrown.

2
  • The testCanBeCreatedFromValidEmailAddress method tests the happy path. It won't pass if an exception was thrown. Given that, you don't have to explicitly check for the exception. Commented Sep 1, 2020 at 21:55
  • 2
    I'd just annotate the test method with @doesNotPerformAssertions Commented Dec 21, 2022 at 2:13

3 Answers 3

34

If an uncaught or unexpected exception is thrown, the test will fail. You don't have to do anything special, just run the code being tested. If there are no other assertions in the test method, you'll also have to do $this->expectNotToPerformAssertions(); or you'll get a warning that the test doesn't perform any assertions.

public function testCannotBeCreatedFromInvalidEmailAddress(): void
{
    $this->expectNotToPerformAssertions();
    Email::fromString('invalid'); // If this throws an exception, the test will fail.
}
Sign up to request clarification or add additional context in comments.

4 Comments

Maybe my question wasn't clear (or I misunderstand your answer). To make it explicitly clear, what I want to test is Email::fromString('[email protected]') ? i.e. I was hoping to find an exceptionNotThrown() method (doesn't exist as far as I can tell).
You don't need something like ExceptionNotThrown() because that's the default behavior. Just run the code and the test will pass if no exception is thrown.
This answer works. But if someone also need another approach, maybe he can try this one too. guh.me/how-to-assert-that-an-exception-is-not-thrown-on-phpunit or this one ashleysheridan.co.uk/blog/… (I don't prefer the second one tho)
"If an uncaught or unexpected exception is thrown, the test will fail." That’s false, the test will error, which is both different from failing and undesirable.
2

PHPUnit does not provide a way to check that no exception is thrown.

Contrary to what the accepted answer states, if an uncaught exception or error is thrown, your test will not fail, it will error instead.

Errors and failure are different, both in meaning and in function.

A test failure means your application code is not behaving as you would like it to do, e.g. does not conform to your project requirements.

Errors mean something is unexpectedly wrong with your code. It can be your application code, your configuration, or that something is bad with your tests.

While you could rely on errors to test your code, that’s what assertions (and, test failures) are for in the first place and it just seems like bad practice to me. Not only are errors categorised differently from test failures, the return code of PHPUnit is even different: 1 for test failures, 2 for errors during testing.

Comments

2

I prefer a combination of the two answers and the linked solution from a comment of Alex' answer:

public function testCannotBeCreatedFromInvalidEmailAddress(): void
{
    $this->expectNotToPerformAssertions();

    try {
        Email::fromString('invalid');
    } catch (\Throwable $e) {
        $this->fail('Case invalid has no email.');
    }
}

As opposed to the linked solution of the comment I don't like to do "empty" assertions (doing an assertTrue(true) after fromString). I see this use case as perfect for expectNotToPerformAssertions() (as propesed by Alex' answer, but without try/catch for clean PHPUnit state handling, which was claimed by Mat).

Comments

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.