rockstardev
rockstardev

Reputation: 13537

PHPUnit mock a controller with reference parameter?

I have a class:

class Hello {
    function doSomething(&$reference, $normalParameter) {
       // do stuff...
    }
}

Then I have a controller:

class myController {
    function goNowAction() {
         $hello = new Hello();
         $var = new stdClass();
         $var2 = new stdClass();
         $bla = $hello->doSomething($var, $var2);
    }
}

The "goNow" action I call using my tests like so:

$this->dispatch('/my/go-now');

I want to mock the "doSomething" method so it returns the word "GONOW!" as the result. How do I do that?

I've tried creating a mock

$mock = $this->getMock('Hello ', array('doSomething'));

And then adding the return:

$stub->expects($this->any())
         ->method('discoverRoute2')
         ->will($this->returnValue("GONOW!"));

But I'm stumped as to how to hook this up to the actual controller that I'm testing. What do I have to do to get it to actually call the mocked method?

Upvotes: 1

Views: 635

Answers (2)

Sven
Sven

Reputation: 70913

Your example code does not explain your problem properly.

Your method allows two parameters, the first being passed as a reference. But you create two objects for the two parameters. Objects are ALWAYS passed as a reference, no matter what the declaration of the function says.

I would suggest not to declare a parameter to be passed as a reference unless there is a valid reason to do so. If you expect a parameter to be a certain object, add a typehint. If it must not be an object, try to avoid passing it as a reference variable (this will lead to confusing anyways, especially if you explicitly pass an object as a reference because everybody will try to figure out why you did it).

But your real question is this:

But I'm stumped as to how to hook this up to the actual controller that I'm testing. What do I have to do to get it to actually call the mocked method?

And the answer is: Don't create the object directly in the controller with new Hello. You have to pass the object that should get used into that controller. And this object is either the real thing, or the mock object in the test.

The way to achieve this is called "dependency injection" or "inversion of control". Explanaitions of what this means should be found with any search engine.

In short: Pass the object to be used into another object instead of creating it inside. You could use the constructor to accept the object as a parameter, or the method could allow for one additional parameter itself. You could also write a setter function that (optionally) gets called and replaces the usual default object with the new instance.

Upvotes: 1

Steven Scott
Steven Scott

Reputation: 11260

You could create a mock for the reference, or if it is just a simple reference as your code shows, send a variable. Then the normal mock call may be called and tested.

$ReferenceVariable= 'empty';

$mock = $this->getMock('Hello ', array('doSomething'));
$stub->expects($this->any())
     ->method('discoverRoute2')
     ->will($this->returnValue("GONOW!"));

$this->assertEquals('GONOW!', $stub->doSomething($ReferenceVariable, 'TextParameter'));

Upvotes: 1

Related Questions