简体   繁体   中英

Unit Test for Parent Method Call

Weirdly, not only have I failed to find an answer for this, but also this question doesn't seem to be a concern at all. It is for me, however.

How do you set an expectation of a parent method call with PHPUnit?

I already read the answers and discussions, the point of which is that "you don't need to test for that, you only test that it works". This is entirely wrong. Unit tests are there to test for the internals of the implementation. Testing that "it works" is for functional tests.

More context: I have an exception implementation, which accepts additional arguments to the constructor, and therefore naturally needs to call the parent::__construct() method, forwarding the message, the code, and the inner exception to it. I know that the parent method works, and I don't need to test it: it's a native PHP class, and even if it wasn't, that code is not in the class I am testing. So, all I need is to set some expectations, one of which is that the parent __construct() method is called with the appropriate args. Of course, the constructor is not the only case where this could be necessary.

Any ideas?

As for the PHPUnit and checking for parent methods calls it is unfortunately impossible. You can always create a mock of a class and check if the parent constructor was called but then you are only testing a mock and not your actual class. And this is definitely not what you want. As someone else already pointed out in another answer: you don't mock or stub subject-under-test ( https://stackoverflow.com/a/6711948/4737924 ).

You are saying that you need to know is that the parent constructor was called. I think it is only a technical detail of the implementation. What you really need to know is if your exception behaves as it needs to behave.

Take a look at the following example:

class MyException extends Exception
{
    private $somethingElse;

    public function __construct(string $message, int $code, Throwable $previousException, SomethingElse $somethingElse)
    {
        $message = $message . ' - my additional message';
        parent::__construct($message, $code, $previousException);

        $this->somethingElse = $somethingElse;
    }

    public function getSomethingElse(): SomethingElse
    {
        return $this->somethingElse;
    }
}

class MyExceptionTest extends TestCase
{
    public function testIsCreatedProperly(): void
    {
        $previousException = new Exception();
        $somethingElse = new SomethingElse();

        $exception = new MyException('My message', 100, $previousException, $somethingElse);

        $this->assertSame('My message -  my additional message', $exception->getMessage());
        $this->assertSame(100, $exception->getCode());
        $this->assertSame($previousException, $exception->getPrevious());
        $this->assertSame($somethingElse, $exception->getSomethingElse());
    }
}

Here only the behavior matters. It is really irrelevant how it happened that the message is correct, or the code or the previous exception. All it matters is if it behaves as you wanted it to behave. You don't need to explicitly check if the parent constructor was called. You know it was called because the output is as expected.

You can create smth like the following trait, and use it in all classes, where you need to mock parent method call:

trait ParentCallTestable
{
    /**
     * Call parent methdod
     * @param  string $method  Method name to call
     * @return mixed
     */
    public function parent($method, ...$args)
    {
        return parent::$method(...$args);
    }
}

Then in a class, that uses this trait, standard parent call

$result = parent::fooMethod($arg1, $arg2);

is replaced with

$result = $this->parent('fooMethod', $arg1, $arg2);

and you can mock it easily.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM