Jan0707 / phpstan-prophecy

MIT License
101 stars 29 forks source link

Support for ProphecySubjectInterface::getProphecy() #228

Open weeman1337 opened 4 years ago

weeman1337 commented 4 years ago

There should be support for ProphecySubjectInterface::getProphecy().
It should be handled the same way as prophesize().

weeman1337 commented 4 years ago

Example:

TestInterface

<?php

declare(strict_types=1);

namespace Test;

interface TestInterface
{
    public function func(): string;
}

ExampleTest:

<?php

declare(strict_types=1);

namespace Test;

use PHPUnit\Framework\TestCase;
use Prophecy\PhpUnit\ProphecyTrait;
use Prophecy\Prophecy\ProphecySubjectInterface;

class ExampleTest extends TestCase
{
    use ProphecyTrait;

    /**
     * @var ProphecySubjectInterface|TestInterface
     */
    private $test;

    public function setUp(): void
    {
        parent::setUp();

        $this->test = $this->prophesize(TestInterface::class)
            ->reveal();
    }

    public function testFuncHello(): void
    {
        $this->test->getProphecy()
            ->func()
            ->willReturn('hello');

        self::assertSame('hello', $this->test->func());
    }
}

PHPStan output:

 Line   test/ExampleTest.php                                                                                                            
 ------ -------------------------------------------------------------------------------------------------------------------------------- 
  30     Call to an undefined method Prophecy\Prophecy\ProphecyInterface::func().                                                        
  30     Call to an undefined method Prophecy\Prophecy\ProphecySubjectInterface|Test\TestInterface::getProphecy().  
  34     Call to an undefined method Prophecy\Prophecy\ProphecySubjectInterface|Test\TestInterface::func().         
stof commented 7 months ago

This @var ProphecySubjectInterface|TestInterface should actually be @var ProphecySubjectInterface&TestInterface

alexander-schranz commented 6 months ago

Shouldnt that be:

    /**
     * @var ObjectProphecy<TestInterface>
     */
    private $test;

But looks like getProphecy is returning still a Prophecy\Prophecy\MethodProphecy (my issue forgot the reveal call), still you currently not require to do reveal in the setUp and can so do:

        $this->test
-           ->getProphecy()
            ->func()
            ->willReturn('hello');

As I'm not sure if there is any easy way to fix the getProphecy

stof commented 6 months ago

My own usage is indeed to store the ObjectProphecy instead of the revealed double, which makes it easier.

alexander-schranz commented 6 months ago

@stof Same for me make it a lot easier.

I'm thinking of that reveal returns ProphecySubjectInterface<T>:

/**
 * @template T of object
 * @template-implements ProphecyInterface<T>
 */
class ObjectProphecy implements ProphecyInterface
{
    /**
     * @return ProphecySubjectInterface<T>
     */
    public function reveal()
    {
    }
}

And ProphecySubjectInterface has extends to T:

/**
 * @template T of object
 * @extends T
 */
interface ProphecySubjectInterface
{
    /**
     * @return ObjectProphecy<T>
     */
    public function getProphecy()
    {
    }
}

But not sure and did not yet test it.