PHPUnit: How to test a method which calls another function declared in different file

Viewed 2827

I'm trying to test a method using PHPUnit, where it calls another function (standalone function, without class), which resides in different file which does a some pretty good calculation and returns a object.

This is my actual main code:

class CreateRecords
{
    public function createEntities($details)
    {
        if (trim($details['username']) == "") {
            $this->result = "Username is empty.";
        } else {
            $this->result = create_record($Details['username']);
        }       
        return $this->result;
    }
}

This create_record function, (standalone function, without class), which is core function, resides in separate file and it does pretty good calculations (calls lots of other methods/functions) and returns object, whether it is successful or not.

I can mock the createEntities method, but I want to mock the create_record function, which does all the computations and returns the result.

I have seen few posts which has a somewhat similar scenario,

phpunit testing method that calls other class methods which need mock

PHPUnit mock method used in another class

But I am unable to understand, how to mock standalone function which is declared in some different file.

3 Answers

You can create new method that will be returning result from outside function. Then you can mock this new method

class CreateRecords
{
    public function createEntities($details)
    {
        if (trim($details['username']) == "") {
            $this->result = "Username is empty.";
        } else {
            $this->result = $this->createRecord($Details['username']);
        }       
        return $this->result;
    }

    public function createRecord($username){
    return create_record($username);
    }
}

Namespaces in PHP5.3+ offer an excellent solution to this, which allows you to override built-in functions in your current namespace.

Longer term, refactoring the global create_record() into a class which can be injected into your CreateRecords class, and then mocked, would be a good way to go. In this instance, the global function would just be a simple wrapper to call the class until the entire application was updated.

To re-create the create_record($username) is not hard though, and useful for tests. The same technique can be used to override the global time() function as well.

Within the test-file, add a new (but local) namespace:

<?php
namespace Test\DatabaseAccess;

use DatabaseAccess\CreateRecord;
use PHPUnit\Framework\TestCase;

namespace DatabaseAccess {
    function create_record($username)
    {
        // pretend to do something
    }
}

class CreateRecordTest extends TestCase
{
     // test to check CreateRecord class
     // which should call the `create_record`, above
}

This is the same technique that the SymfonyBridge system uses to create ClockMock - which dynamically adds time(), sleep(), etc to the namespace that you are unit-testing (in this example, the namespace DatabaseAccess\CreateRecord, not the Test\ prefixed namespace). ClockMock (and DnsMock) does it with an eval() call, but since you know the namespace explicitly, you can write it into the test file itself for clarity.

Related