47

Lets say I have a class that contains a function that uses type hinting like this:

class Testable 
{
    function foo (Dependency $dependency) 
    {

    }
}

And I want to unit test this class Testable using this code:

$dependencyMock = $this->getMockBuilder('Dependency')
        ->disableOriginalConstructor()
        ->getMock();


$testable = new Testable($dependencyMock);

If I use PHPUnit to create a stub of $dependency and then try to call the function foo using this mock (like above), I will get a fatal error that says:

Argument 1 passed to function foo() must be an instance of Dependency, instance of Mock_Foo given

How can I unit test this function with PHPUnit and still stub $dependency?

elixenide
  • 44,308
  • 16
  • 74
  • 100
user3009816
  • 797
  • 2
  • 8
  • 16

3 Answers3

49

Use full namespace when you use mocking, it will fix the mockery inheritance problem.

$dependencyMock = $this->getMockBuilder('\Some\Name\Space\Dependency')
    ->disableOriginalConstructor()
    ->getMock();
$testable = new Testable($dependencyMock);
Shakil Ahmed
  • 1,481
  • 2
  • 19
  • 26
  • Thanks for this. I don't think the fact that you need the full namespace was clear in the documentation. Once I saw your post I thought "oh! of course" but I def didn't get that from the documentation. – Chris Schmitz Jun 01 '16 at 21:27
  • For a more robust way to do this in PHP 5.5+ https://stackoverflow.com/questions/20446771/how-to-unit-test-functions-that-use-type-hinting/33159534#33159534 – GWed Dec 25 '18 at 11:10
21

The easiest way to use a full namespace in PHP 5.4+ is with the class static method:

SomeClass::class

So in the OP's example:

$dependencyMock = $this->getMockBuilder(Dependency::class)
    ->disableOriginalConstructor()
    ->getMock();
$testable = new Testable($dependencyMock);

This makes refactoring with an IDE a lot easier

GWed
  • 15,167
  • 5
  • 62
  • 99
5

My explication for the Shakil's answer:

I had the same problem.

Following the symfony2 cookbook, I created a mock of

\Doctrine\Common\Persistence\ObjectManager

and my service constructor was:

use Doctrine\ORM\EntityManager;

/* ... */

public function __construct(EntityManager $oEm)
{
    $this->oEm = $oEm;
}

So I created my unit test (following symfony2 cookbook):

$entityManager = $this->getMockBuilder('\Doctrine\Common\Persistence\ObjectManager')
    ->disableOriginalConstructor()
    ->getMock();

$myService = new MyService($entityManager);

Then I had the error:

Argument 1 passed to MyService::__construct() must be an instance of Doctrine\ORM\EntityManager, instance of Mock_ObjectManager_f4068b7f given

First I though that type hinting was incompatible with unit tests, because a mock instance was passed to the constructor instead of an instance of EntityManager.

So after some research, the class Mock_ObjectManager_f4068b7f is in fact a dynamic class extending the class of your mock (in my case Doctrine\ORM\EntityManager), so the type hinting is not a problem and works well.

My solution was to create a mock of Doctrine\ORM\EntityManager instead of \Doctrine\Common\Persistence\ObjectManager:

$entityManager = $this->getMockBuilder('\Doctrine\ORM\EntityManager')
    ->disableOriginalConstructor()
    ->getMock();

$myService = new MyService($entityManager);

I am just beginning with unit tests, so you may find my explication evident :p

Alcalyn
  • 1,531
  • 14
  • 25