34

I'm looking for a way to mock an object and populate its properties. Here's an example of a method who uses a property of another object:

class MyClass {

    private $_object;

    public function methodUnderTest($object) {
        $this->_object = $object;
        return $this->_object->property
    }
}

To Unit Test this method I should create a mock of $object with the getMockBuilder() method from PHPUnit. But I can't find a way to mock the properties of the $object, just the methods.

Beittil
  • 117
  • 10
Riccardo Cedrola
  • 1,270
  • 1
  • 10
  • 16
  • 2
    Why don't you just set those properties without mocking them? – akond Jun 15 '17 at 16:15
  • I actually tried to do that, but don't know why it never worked, until today. Thanks for the heads up, you can actually set properties of mocked objects as you'd normally with an object – Riccardo Cedrola Jun 16 '17 at 08:11
  • Luckily I'm not completely crazy, the simple solution you proposed works fine with most of my classes, but for a couple, it doesn't work and it just returns `NULL` – Riccardo Cedrola Jun 16 '17 at 08:33

2 Answers2

63

To add properties to a mocked object, you just set them as you'd normally do with an object:

$mock = $this->getMockBuilder('MyClass')
             ->disableOriginalConstructor()
             ->getMock();

$mock->property = 'some_value';

$mock->property will now return 'some_value'

Thanks to akond

P.s. for my project, this doesn't work with some classes, and when I try to call $mock->property it just returns NULL

hatef
  • 5,491
  • 30
  • 43
  • 46
Riccardo Cedrola
  • 1,270
  • 1
  • 10
  • 16
  • 5
    Make sure you don't use magic methods inside the original class. – vivanov Dec 30 '17 at 15:13
  • 7
    If your class uses magic methods you actually have to mock the `__get()` method and make it return the desired output. This was the reasons behind the failure in some of my classes – Riccardo Cedrola Sep 27 '18 at 14:40
  • 1
    `disableOriginalConstructor()` needs parenthese to indicate it is a method, not a property. Otherwise you will get an error – gArn Oct 22 '18 at 16:07
  • @RiccardoCedrola how would you create such mocked `__get()` construct? – powtac Jan 14 '20 at 13:52
  • 2
    @powtac as you would normally do with any other method, you just mock the `__get()` and make it return the desired value. The problem is that this approach isn't really dynamic and it's hard to mock if is used multiple times inside the method you are testing. I might be wrong but I think there's a way where you mock a method by saying "when you receive A return B, when you receive X return Y" – Riccardo Cedrola Jan 16 '20 at 09:15
  • 16
    If your class uses magic methods, you can mock __get for the specific property like this: `$mock->method('__get')->with('propname')->willReturn('fakevalue');` – Collin Krawll Jan 25 '20 at 20:01
  • @CollinKrawll This seems not to work when you need to mock __get for two or more different arguments? – Olle Härstedt May 28 '20 at 09:08
  • @OlleHärstedt Try PHPUnit's return value map: https://stackoverflow.com/a/30482721/3649573 – AlbinoDrought Jul 14 '20 at 23:42
  • 2
    Doesn't seem to work for `readonly` properties – jave.web Aug 30 '22 at 14:59
21

If you have classes with magic methods you can use:

$mock->method('__get')->with('property')->willReturn('value');
Jsowa
  • 9,104
  • 5
  • 56
  • 60
  • 2
    How would we deal with code that is getting multiple properties? I duplicated multiple lines for each property bu have an exception of `Expectation failed for method name is "__get"...` – bilogic Feb 12 '23 at 13:41
  • 1
    This is also valid for private properties exposed by a publicmethod: $mock->method('publicmethod')->willReturn('value'); – alboforlizo Apr 14 '23 at 14:04