Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How would you test PHP code which uses global variable?

Suppose the class Account (in account.php file) which uses a $DB variable to store data in the database. The $DB variable is initialized in another globals.php file.


/**** globals.php ****/

$DB = PDO (....);

/**** account.php ****/

public function create($data) {
global $DB;
....
}

Now, suppose that you want to test a function in account class called create using PHPUnit test. How would you initialize the $DB object?

like image 613
arash Avatar asked Jun 28 '26 03:06

arash


2 Answers

Ideally, you should not use globals you are hiding dependencies and can cause things to happen due to changes in the global variable.

It would be possible for your to hack in and replace your variable so that you can mock it. You can use the setUp method to store what is in the global $DB and then restore it in the teardown() method. This way, you don't accidentally break some other test with your mock.

public function setUp() {
    global $DB;
    $this->oldDB = $DB;
}

public function testCreate() {
    $mockDB = $this->getMock('PDO') ... //More steps to complete mock object

    global $DB;
    $DB = $mockDB;

    //Rest of Test here
}

public function teardown() {
    global $DB;
    $DB = $this->oldDB;
}

Now just because you CAN do this, doesn't mean you SHOULD do this. It would be better to refactor your code so that it doesn't depend upon the global scope. But if that isn't an option at this time, here is a work around that will at least make the test usable until you refactor.

like image 175
Schleis Avatar answered Jun 29 '26 16:06

Schleis


How would you initialize the $DB object?

Just initialize it as usual:

class TestCase extends PHPUnit_Framework_TestCase
{
    function testDatabaseConnection()
    {
        require 'path/to/globals.php'; // take "global" variable in local scope
        $GLOBALS['DB'] = $DB; // make it globally available

        $account = new account();
        $this->assertInstanceOf('account', $account);

        $this->assertTrue($account->create("data"));

        ...
    }
}

Then start with testing and get a feeling how the code behaves when you test it thoroughly.

You can then consider to initialize the database connection before the test-class is created once, so that you can write the test-routines faster.

  • Chapter 4. Fixtures

Later then you probably write yourself a helper method to instantiate the account class.

That perhaps is then also the time where you realize that injecting the database connection object into the constructor might not be that bad as well.

like image 31
hakre Avatar answered Jun 29 '26 15:06

hakre