PHPUnit Mock an object's properties
Asked Answered
B

2

39

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.

Bozovich answered 15/6, 2017 at 15:7 Comment(3)
Why don't you just set those properties without mocking them?Repulsive
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 objectBozovich
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 NULLBozovich
B
65

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

Bozovich answered 16/6, 2017 at 8:15 Comment(9)
Make sure you don't use magic methods inside the original class.Mutation
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 classesBozovich
disableOriginalConstructor() needs parenthese to indicate it is a method, not a property. Otherwise you will get an errorShipmaster
@RiccardoCedrola how would you create such mocked __get() construct?Milkandwater
@Milkandwater 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"Bozovich
If your class uses magic methods, you can mock __get for the specific property like this: $mock->method('__get')->with('propname')->willReturn('fakevalue');Aba
@CollinKrawll This seems not to work when you need to mock __get for two or more different arguments?Takakotakakura
@OlleHärstedt Try PHPUnit's return value map: https://mcmap.net/q/352407/-phpunit-39-s-returnvaluemap-not-yielding-expected-resultsSessoms
Doesn't seem to work for readonly propertiesSyncopate
C
28

If you have classes with magic methods you can use:

$mock->method('__get')->with('property')->willReturn('value');
Capitulation answered 18/6, 2021 at 12:48 Comment(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"...Grog
This is also valid for private properties exposed by a publicmethod: $mock->method('publicmethod')->willReturn('value');Stagnate

© 2022 - 2024 — McMap. All rights reserved.