Mockery & PHPUnit: method does not exist on this mock object

15,638

The error message seems clear to me. You only did setup a expectation for the put method, but not exists. The exists method is called by the class under test in all code paths.

public function testGeneratorFire()
{
    $fileMock = \Mockery::mock('\stats\jway\File');
    $fileMock->shouldReceive('put')->with('foo.txt', 'foo bar')->once();

    //Add the line below
    $fileMock->shouldReceive('exists')->once()->andReturn(false);

    $generator = new Generator($fileMock);
    $generator->fire();
}
Share:
15,638
PeraMika
Author by

PeraMika

Updated on July 15, 2022

Comments

  • PeraMika
    PeraMika almost 2 years

    Can you tell me where's the problem? I have a file GeneratorTest.php with the following tests:

    <?php
    
    namespace stats\Test;
    
    use stats\jway\File;
    use stats\jway\Generator;
    
    class GeneratorTest extends \PHPUnit_Framework_TestCase
    {
    
        public function tearDown() {
            \Mockery::close();
        }
    
        public function testGeneratorFire()
        {
            $fileMock = \Mockery::mock('\stats\jway\File');
            $fileMock->shouldReceive('put')->with('foo.txt', 'foo bar')->once();
            $generator = new Generator($fileMock);
            $generator->fire();
        }
    
        public function testGeneratorDoesNotOverwriteFile()
        {
            $fileMock = \Mockery::mock('\stats\jway\File');
            $fileMock->shouldReceive('exists')
                ->once()
                ->andReturn(true);
    
            $fileMock->shouldReceive('put')->never();
    
            $generator = new Generator($fileMock);
            $generator->fire();
        }
    }
    

    and here are File and Generator classes:

    File.php:

    class File
    {
        public function put($path, $content)
        {
            return file_put_contents($path, $content);
        }
    
        public function exists($file_path)
        {
            if (file_exists($file_path)) {
                return true;
            }
            return false;
        }
    }
    

    Generator.php:

    class Generator
    {
        protected $file;
    
        public function __construct(File $file)
        {
            $this->file = $file;
        }
    
        protected function getContent()
        {
            // simplified for demo
            return 'foo bar';
        }
    
        public function fire()
        {
            $content = $this->getContent();
            $file_path = 'foo.txt';
    
            if (! $this->file->exists($file_path)) {
                $this->file->put($file_path, $content);
            }
        }
    
    }
    

    So, when I run these tests, I get the following message: BadMethodCallException: Method ... ::exists() does not exist on this mock object.

    enter image description here

  • PeraMika
    PeraMika almost 8 years
    One question: when we create a Mock object like this: $fileMock = \Mockery::mock('\stats\jway\File'); then all methods in that Mock object will return NULL by default, right?. If yes, then why we need to setup exists method like you did, it will retun NULL and therefore put method will be executed anyway?
  • Bram Gerritsen
    Bram Gerritsen almost 8 years
    You have to add shouldIgnoreMissing() to let the mock object return null when you didn't setup an expectation. In this case it would be better to be strict and return false, because a boolean value is what the interface of exists defines. If you rewrite if (! $this->file->exists($file_path)) to if ($this->file->exists($file_path) === false)) your unit test will break.
  • alexw
    alexw almost 7 years
    "You only did setup a expectation for the put method, but not exists. The exists method is called by the class under test in all code paths." Could you explain this more? Why do we need to set up test expectations for methods that are called incidentally?
  • Moses Ndeda
    Moses Ndeda over 6 years
    shouldIgnoreMissing() definitely helped me avoid some bottlenecks.