In PHPUnit, how to mock a function that is not part of a class?
P粉216807924
P粉216807924 2023-10-29 15:44:54
0
2
712

The project I'm currently working on contains a mix of object-oriented and procedural PHP code. So I have something like this:

function doStuff($value)
{
    $x = $value + 1;

    return $x;
}

class MyClass
{
    private $field;

    public function setMyValue($amount)
    {
        $this->field = doStuff($amount) + doStuff(2 * $amount);
    }
}

There are several of these dependencies, but very few (you can count them on one hand). However, I need to write a unit test for a class (using PHPUnit) and I don't know how to mock a function from the terminal (in this case doStuff). As far as I know, the mocking functionality in PHPUnit only works with classes.

I would have done it without any mocking, but the problem is that some of these functions do some IO operations; I don't think it's a good idea to not mock them in some way.

How can I solve this problem?

P粉216807924
P粉216807924

reply all(2)
P粉275883973

The only option I see is dependency injection, since your class wants to use resources outside the class. So this breaks some encapsulation rules.

How I've done this in the past is to put these functions in their own class and require/include them, and when setting the test variables, include a base file with the same semi-"mocked" functions, The file returns to a known state.

My other approach is to create a simple UTILITY class that contains all these data functions and then use dependency injection and mocking to test it.

class Utilities
{

    function doStuff($value)
    {
        $x = $value + 1;
        return $x;
    }
}

class MyClass
{
    private $UtilitiesObject;

    private $field;

    public function setMyValue($amount)
    {
//        $this->field = doStuff($amount) + doStuff(2 * $amount);
        $this->field = $this->UtilitiesObject->doStuff($amount) + $this->UtilitiesObject->doStuff(2 * $amount);
    }
}

    // Constructor Injection, pass the Utilities object here
    public function __construct($Utilities = NULL)
    {
        if(! is_null($Utilities) )
        {
            if($Utilities instanceof Utilities)
            {
                $this->SetUtilities($Utilities);
            }
        }
    }

    function SetUtilities(Utilities $Utilities)
    {
        $this->UtilitiesObject = $Utilities
    }

}

test:

class UtilitiesTest extends PHPUnit_Framework_TestCase
{

    // Could also use dataProvider to send different returnValues, and then check with Asserts.
    public function testSetMyValue()
    {
        // Create a mock for the Utilities class,
        // only mock the doStuff() method.
        $MockUtilities = $this->getMock('Utilities', array('doStuff'));

        // Set up the expectation for the doStuff() method 
        $MockUtilities->expects($this->any())
                    ->method('doStuff')
                    ->will($this->returnValue(1));

        // Create Test Object - Pass our Mock as the Utilities
        $TestClass = new MyClass($MockUtilities);
        // Or
        // $TestClass = new MyClass();
        // $TestClass->SetUtilitiess($MockUtilities);

        // Test doStuff
        $amount = 10;   // Could be checked with the Mock functions
        $this->assertEquals(2, $TestClass->doStuff($amount));       // Mock always returns 1, so 1+1=2
    }
}
P粉155832941

You can take advantage of PHP's namespace fallback policy when you call functions from a namespace (defined in the global namespace) and always call them unqualified functions.

This allows you to create mocks by providing functions within the caller's namespace.

To make your life extra easy, I packaged it into a library php-mock- phpunit that can be used with PHPUnit:

namespace foo;

use phpmock\phpunit\PHPMock;

class BuiltinTest extends \PHPUnit_Framework_TestCase
{

    use PHPMock;

    public function testTime()
    {
        $time = $this->getFunctionMock(__NAMESPACE__, "time");
        $time->expects($this->once())->willReturn(3);

        $this->assertEquals(3, time());
    }
}
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template