eloquent / phony

Mocks, stubs, and spies for PHP.
http://eloquent-software.com/phony
MIT License
194 stars 7 forks source link

Final class as return type #237

Closed pmall closed 6 years ago

pmall commented 6 years ago

I expected this to work but it throws Eloquent\Phony\Mock\Exception\FinalClassException:

<?php

final class FinalClass {}

interface SomeInterface
{
    public function someMethod(): FinalClass;
}

$final = new FinalClass;

$handle = mock(SomeInterface::class);

$handle->someMethod->returns($final);

In fact it fails as soon as $handle->someMethod is called. Is it the expected behavior? I assumed it would be ok as I specify the instance to return. What is the workaround for this?

ezzatron commented 6 years ago

Hmm. It definitely should work. I even ran into a similar problem in #231, investigated it, and then couldn't reproduce it.

Could you show me how you're actually calling the mock? That's an important piece of the puzzle here too.

pmall commented 6 years ago

@ezzatron yes I saw #231 so I through the call in my tested code wasn't matching, but it is not the case. It fails as soon as $handle->someMethod is called.

The actual code:

<?php

interface BindingStrategyInterface
{
    // Arguments class is final
    public function bind(Arguments $arguments, array $parameters): Arguments;
}

final class ResolvableValue
{
    private $factory;
    private $parameters;

    public function __construct(callable $factory, array $parameters)
    {
        $this->factory = $factory;
        $this->parameters = $parameters;
    }

    // The method actually being tested with a mock of BindingStrategyInterface
    public function value(BindingStrategyInterface $strategy)
    {
        // ...

        $arguments = $strategy->bind(new Arguments, $this->parameters);

        // ...
    }
}

// The test
use function Eloquent\Phony\Kahlan\mock;

describe('->value()', function () {

    $factory = function () { /* whatever */ };
    $parameters = [ /* Some ReflectionParameter */ ];

    $resolvable = new ResolvableValue($factory, $parameters);

    $strategy = mock(BindingStrategyInterface::class);

    $arguments = new Arguments;

    // This fails as soon as $strategy->bind is called. So it is not a problem of wrong parameter matching.
    // using $strategy->bind->returns($arguments) or $strategy->bind->with('*')->returns($arguments) does nothing, it fails before specifying the matching.
    $strategy->bind->with(new Arguments, $parameters)->returns($arguments);

    $test = $resolvable->value($strategy->get());

    // ...

});
ezzatron commented 6 years ago

Yeah, no worries, I actually just managed to reproduce it myself. I will let you know once I've figured it out. Thanks for the report!

ezzatron commented 6 years ago

Fixed in 3.0.1.

pmall commented 6 years ago

thanks!