4

I am writing some tests for my view helper. This is the first time I would like to do something with mocking objects. I'm using the default PHPUnit mocking Framework.

I have written a function which prepares my mock object:

private function getTestStub(){
    $mockResult = array();
    $mock =  $this->getMock('My\Entity\Product');
    $mock->expects($this->once())
                ->method('getId')
                ->will($this->returnValue(1));
    $mock->expects($this->once())
        ->method('getName')
        ->will($this->returnValue('jan'));
    $mock->expects($this->once())
        ->method('getWoonplaats')
        ->will($this->returnValue('Amsterdam'));
    $mockResult[] = $mock;
    return $mockResult;
}

Now when using this stub for my tests I get the following Error:

Fatal error: Call to undefined method Mock_Product_129abca6::getId()

What Am I doing wrong here?

4
  • 1
    Product::getId exists? It's not a magic method (eg. something generated by __call)? Commented Dec 8, 2013 at 17:07
  • Well it's mocked as you can see in my code Commented Dec 8, 2013 at 17:28
  • 1
    You should post the class (or interfaced) defined at My\Entity\Product. Commented Dec 8, 2013 at 20:24
  • But that class is mocked. what has the real My\Entity\Product to do with it? Commented Dec 8, 2013 at 20:26

1 Answer 1

4

PHPUnit looks at the class you're trying to mock via reflection or get_class_methods.

The classes you mock, if they exist, are extended by the mock objects. Similarly interfaces are implemented. You can see how this works in the code itself. It's bunch of code generation stuff, and if you want to take a look and trace how it works a good starting point is PHPUnit_Framework_MockObject_Generator::generate.

Without seeing your the class you're trying to mock, I'm going to guess that your getters are "magic" methods generated by __call with something like this:

<?php
namespace My\Entity;

class Product
{
    private $data = array();

    public function __call($method, $args)
    {
        $set_or_get = strtolower(substr($method, 0, 3));
        $prop = strtolower(substr($method, 3));
        if ('get' === $set_or_get) {
            return isset($this->data[$prop]) ? $this->data[$prop] : null;
        } elseif ('set' === $set_or_get && isset($args[0])) {
            $this->data[$prop] = $args[0];
        } else {
            throw new \BadMethodCallException();
        }
    }
}

PHPUnit can't really do what you want because the methods you're trying to call don't actually exist and the magic __call doesn't work as expected. Either because PHPUnit uses that method itself something else (you'd have to dig in to find it). To get around this, you need to tell PHPUnit which methods you want to include in your mock.

// the second argument let's you define methods
$mock =  $this->getMock('My\Entity\Product');
// try doing this instead
$mock =  $this->getMock('My\Entity\Product', array('getId', 'getName', 'getWoonplaats'));

This question also has some good examples of the above and workarounds.

Sign up to request clarification or add additional context in comments.

2 Comments

Hmm i didn't understand from the PHPUnit manual that Mocking works like this. So If the mocket method does not exist in the actual class then it won't work and it will say that the method is undefined.
Thanks for the answer, it helped when I accidentally mocked 'foo\service\foobar` instead of foo\entity\foobar. Perhaps a TLDR could improve this answer: method('foo')->willReturn('bar') will not do anything if the class being mocked does not define that method.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.