用PHP嘲弄嘲笑具体类的错误

The class under test is as follows:

class ElasticSearchInstaller
{
    /**
     * Version of ElasticSearch 
     *
     * @var \ElasticSearch\Requierments
     */
    protected $requirements;        

    /**
     * Object Constructor.
     * 
     * @param Requierments $requirements 
     */
    public function __construct(Requirements $requirements)
    {
        $this->requirements = $requirements;
    }

    /**
     * Set requirements
     *
     * @return bool
     */
    public function canInstall() 
    {
        return ($this->isInstalled() && $this->requirements->checkRequirements());
    }   
}

The test looks as follows:

class ElasticSearchInstallerTest extends TestCase
{
    /** @test **/
    public function it_can_check_if_its_installable()
    {
        $requirements = m::mock('alias:ElasticSearch\Requirements');

        $requirements->shouldReceive('checkRequirements')->once()->andReturn(true); 

        $installer = new ElasticSearchInstaller($requirements);

        $this->assertInternalType('bool', $installer->canInstall());
    }    
}

Although for some reason it does not pass, as the mock says checkRequirements() is not being called and expects to be called once.

1) ElasticSearchInstallerTest::it_can_check_if_its_installable
Mockery\Exception\InvalidCountException: Method checkRequirements() from App\ElasticSearch\Requirements should be called
 exactly 1 times but called 0 times.

EDIT The problem was, within canInstall() for some reason if $this->isInstalled() is called before $this->requirements->checkRequirements() such as in the code above. It errors. if its swapped to:

public function canInstall() 
{
    return ($this->requirements->checkRequirements() && $this->isInstalled() );
}  

...it passes! wtf?

The issue is that such boolean conditions can be shortcut -if the first part of an && (and) condition is FALSE, the whoe condition itself cannot become true, and so the rest of the parts of the conditional is skipped. The function call was skipped, hence, run 0 times.

Since, in that specific test, you only only currently testing the condition as a whole, you can mock the rest of the ElasticSearchInstaller and do much the same:

$searchInstaller->shouldReceive('isInstalled')->andReturn(true); 

The mock would fall back to use the original canInstall(), and so run the condition. It's still almost a too-simple-to-fail case though - I'd concentrate elsewhere for testing.