1

I've been searching for a way to use a string instead of an integer for the code of a custom exception, but much to my surprise this seems impossible to do!

I'd like to be able to throw such an exception:

throw new CustomException( "user_not_found", "User not found" );

so then I can test it as follows with PHPUnit:

$this->expectExceptionCode( "user_not_found" ); 
$User = new User(100); // 100 is a valid id for a non-existing user 
$User->delete_user(); // This method throws the above CustomException

What I've been doing so far is passing a custom context array to the exception:

throw new CustomException( "User not found", [ "debug" => "user_not_found" ] );

and then my test looks like this:

try {
    $User = new User(100);
    $User->delete_user();
} catch( \Throwable $e ) {
    $this->assertEquals( "user_not_found", $e->getContext()["debug"] );
}

But I would really prefer the first solution because it looks cleaner to me.

grazdev
  • 1,082
  • 2
  • 15
  • 37
  • 1
    When you extend `Exception`, you can add whatever custom fields you want, including in the constructor. If you want to use the numeric code, you can do that with class constants, and you can use subclasses to hide these from callers, so UserNotFound automatically calls the parent with 1001 or whatever. Or, ignore the codes and inspect on the specific exception by type – Chris Haas Dec 04 '21 at 12:29

1 Answers1

0

First, create base exception, like:

class MyBaseException extends \Exception {
    public function __constructor(string $key, string $message) {
        parent::__construct($message, ['debug' => $key]);
    }
}

Then, implement other exceptions, like:

class UserNotFoundException extends MyBaseException {
    public function __constructor() {
        parent::__constructor('user_not_found', 'User not found');
    }
}

Finally, write tests for class (not key and/or code), like:

$this->expectException(UserNotFoundException::class);

// Your throwing code 

$User = new User(100);
$User->delete_user();

See also: How to assert that an exception was thrown?

Top-Master
  • 7,611
  • 5
  • 39
  • 71
  • Yes, this is a solution but in this way I would have to create a custom exception for every error I want to test, while I would really prefer to have only one custom exception to reuse with different code/message for each error. – grazdev Dec 04 '21 at 13:34
  • @grazianodev for that rename`MyBaseException` to `CustomException` and throw. So that at least **throwing** is the way you want, but `PHPUnit` does not support **catching** the way you want. – Top-Master Dec 04 '21 at 13:50
  • @grazianodev of course **for you** it seems bad to have a `class` for each message; that's because you `throw` just once, but **other developers** will hate you, because even if anyone is trying to `throw` exact same `Exception`, they are forced to copy/paste. And whenever wanting to slightly change message, they are forced to do search-and-replace in entire folder. – Top-Master Dec 04 '21 at 14:02
  • Haha well actually I'm a self-taught programmer working on a personal project, with no intention to work with other developers. Anyway, I have exceptions that I throw only once, wouldn't it be overkill to create a very specific custom exception to throw only once? – grazdev Dec 04 '21 at 14:24
  • 1
    @grazianodev "following correct structure and/or design-pattern" is never overkill (but if concerned about speed, PHP has opcache-extension, try that out if possible). – Top-Master Dec 04 '21 at 14:58