15

I am learning and exploring applications of PHPUnit with PHP 5.2.9 and have run into the globals issue. I have set $backupGlobals to FALSE, included the doc '@backupGlobals disabled' and this doesn't seem to affect the behaviour of PHPUnit's backing up of the globals. Is there something I'm missing? Do I need to alter PHPUnit's xml file? Create a bootstrap?

config.php:

$testString = 'Hello world!'; 

basicApp.php:

require ('D:\data\clients\security.ca\web_sites\QRASystems.com\wwwroot\__tests\BasicApp\config.php'); class BasicApp { public $test; public function __construct() { global $testString; $this->test = $testString; } public function getTest() { return $this->test; } public function setTest($test){ $this->test = $test; } 

BasicAppTest.php:

require ('D:\data\clients\security.ca\web_sites\QRASystems.com\wwwroot\__tests\BasicApp\BasicApp.php'); class BasicAppTest extends PHPUnit_Framework_TestCase{ protected $testClass; protected $backupGlobals = FALSE; protected $backupGlobalsBlacklist = array('testString'); public function SetUp(){ $this->testClass = new BasicApp; $this->testClass->bootstrap(); } public function testGlobal(){ echo $this->testClass->getTest(); $this->assertNotNull($this->backupGlobals); $this->assertFalse($this->backupGlobals); $this->assertNotEmpty($this->testClass->test); } public function testMethods(){ $this->testClass->setTest('Goodbye World!'); echo $this->testClass->getTest(); $this->assertNotNull($this->backupGlobals); $this->assertNotNull($this->testClass->test); if (empty($this->testClass->test)) echo 'Method set failed!'; } } 

testGlobal() fails on $this->assertNotEmpty($this->testClass->test), indicating that $this->backupGlobals is set to FALSE and that globals are still being back up by PHPUnit.

EDIT: I got this working by making the following changes-

BasicAppTest.php:

 protected $backupGlobals = FALSE; <- REMOVED protected $backupGlobalsBlacklist = array('testString'); <- REMOVED 

config.php:

global $testString; <- ADDED $testString = 'Hello world!'; 

I am dumbfounded that this hasn't been covered before somewhere!

3
  • 1
    Is the line global $testString; needed? My understanding is that the global keyword at the global scope has no effect because you're already in the global scope. Commented Mar 15, 2012 at 3:51
  • 1
    @DavidHarkness You have no way of knowing when you are writing the code whether you are in the global scope or not. Just because you are in the main scope of the file and not inside a function doesn't mean that you will be in global scope at runtime. If that file is included within a function, you will actually be in the scope of that function, even in the main scope of the file. For this reason it is generally considered best practice to always declare a variable you want to be global as such with the global keyword. That way you know it will be global no matter what. Commented Jan 19, 2016 at 15:26
  • Does this answer your question? global variables are null when using PHPUnit Commented Jan 27, 2024 at 0:17

2 Answers 2

12

In your test case you are defining a new $backupGlobals property that PHPUnit won't see. Since the property is protected, you could set it to false in the constructor, but PHPUnit uses its constructors to pass information on how to run the test method. Instead, create a phpunit.xml configuration file to set the backupGlobals property to false.

<phpunit backupGlobals="false"> <testsuites> <testsuite name="Test"> <directory>.</directory> </testsuite> </testsuites> </phpunit> 
Sign up to request clarification or add additional context in comments.

8 Comments

Thank you very very much. I've banged my head against this for the better part of last week looking for the missing piece. I assumed that protected properties could be over-written by sub-classes! Alright, quick follow-up: where does the .xml file go?
I put it in the root of my tests folder, but it can go anywhere. You can point phpunit to it using the --configuration parameter (I think -c was added as a shortcut recently). If it's in the current directory and named phpunit.xml, PHPUnit will pick it up automatically.
...somehow, this slipped the net. If you're going to use global variables with PHPUnit, you need to both declare the variable global in the global space (and assign the value to it) and declare its inclusion where it is needed locally. The default behaviour with PHP is that variables initialized in the global space are made global. PHPUnit changes this default!
@Malovich - I'm not sure I follow. With PHP (and I don't see how PHPUnit could change this), you can read global variables in a function without declaring them as such, but you must declare them using global if you want to write to them.
As you say,the global keyword normally is used only in a local scope (be it a function, method or namespace) to make a variable initialized in the global namespace available within that local scope. PHP's default behaviour with any variable initialized in the global scope is to make it available as a global variable.
|
3

In your edits and comments you've pointed out one workaround for the problem (explicitly declaring globals within the tested application). In onlab's comment to a PHPUnit issue he explains the behavior: when including a file in a function, PHP puts globals from the included files in the function's scope. PHPUnit loads files in a function, and though it tries to extract globals, it fails in the cases I've tried.

Unfortunately, I haven't been able to reproduce the problems of my legacy system in minimal test cases (and I had trouble understanding yours), and so I can't really confirm the explanation. But his suggested workaround helped me: supply a bootstrap file using the --bootstrap option; in it, declare every global used by the tested parts of your application. This avoids the need to modify the application in order to test it. Here's onlab's example from GitHub:

phpunit --bootstrap bootstrap.php test-path 

with bootstrap.php:

global $my, $system, $globals, $here; require_once("/path/to/my/system/bootstrap.php"); 

1 Comment

Both in the link and in your copy&paste there is a small typo. It should read: global $my, $system, $globals, $here;, i.e. without the trailing 's'.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.