How to unit test SugarCRM modules? - SugarForge
How to unit test SugarCRM modules? - SugarForge
How to unit test SugarCRM modules? - SugarForge
Create successful ePaper yourself
Turn your PDF publications into a flip-book with our unique Google optimized e-Paper software.
<strong>How</strong> <strong>to</strong> <strong>unit</strong> <strong>test</strong> <strong>SugarCRM</strong> <strong>modules</strong><br />
Ingo Jaeckel (ingo.jaeckel@student.hpi.uni-potsdam.de)<br />
January 27, 2009<br />
1 Introduction<br />
You may experienced that it is not possible <strong>to</strong> write and execute <strong>unit</strong> <strong>test</strong>s as usual for<br />
<strong>SugarCRM</strong> using the “usual suspects” (PHPUnit [1], SimpleTest [2]). I even developed a<br />
tiny <strong>unit</strong> <strong>test</strong>ing “framework” on my own and tried <strong>to</strong> <strong>unit</strong> <strong>test</strong> my <strong>SugarCRM</strong> <strong>modules</strong><br />
with it. Unfortunately, this was without any success.<br />
<strong>How</strong>ever, there are other people having the same or related issues (see PHP bugtracker<br />
[3], [4]).<br />
I think that the way PHP handles global 1 variables and the way you execute the<br />
<strong>SugarCRM</strong> code during the <strong>unit</strong> <strong>test</strong> 2 is the reason for these problems 3 .<br />
This document shows you how <strong>to</strong> workaround most problems and be able<br />
<strong>to</strong> <strong>unit</strong> <strong>test</strong> your code.<br />
2 What’s the problem (short version)<br />
<strong>SugarCRM</strong> uses a lot global variables (around 280). If you write some PHP <strong>unit</strong><br />
<strong>test</strong>cases you will write some methods. Within these methods you will write some<br />
require_once statements <strong>to</strong> load the classes or methods you need. Or you wrote those<br />
statements above the <strong>test</strong> case class that you defined.<br />
<strong>How</strong>ever, when PHPUnit executes your <strong>test</strong>s after each other, something similiar<br />
will happen: The <strong>SugarCRM</strong> code that will be executed in a <strong>test</strong>method will at some<br />
point do some require_once calls <strong>to</strong> load the classes, methods or variables it needs.<br />
Unfortunately those variables will never be “real globals” - Meaning if you try <strong>to</strong> get<br />
access <strong>to</strong> them using the global statement, they will just be uninitialized 4 .<br />
This means <strong>SugarCRM</strong> tries <strong>to</strong> create global variables at some point which does not<br />
work, because we are in a <strong>test</strong>method and cannot create variables with global scope.. As<br />
a consequence NOTHING really works. You will get <strong>to</strong>ns of errors. If you fixed one<br />
1 See the manual [5] for more information about the global statement in PHP.<br />
2 See how Joel Hainley works around this problem by moving all global variables <strong>to</strong> a single<strong>to</strong>n [6].<br />
3 Sebastian Bergmann gives a few hints about PHPUnit and globals on [7].<br />
4 Sometimes () it is possible <strong>to</strong> access those variables with the $GLOBALS array. But you probably do<br />
not want <strong>to</strong> modify any <strong>SugarCRM</strong> code just <strong>to</strong> get your <strong>unit</strong> <strong>test</strong>ing stuff working.<br />
1
3 The solution..<br />
problem (by modifying <strong>SugarCRM</strong> core files which is a great idea) you will constantly<br />
get other problems. Yehaa!<br />
3 The solution..<br />
.. is actually quite simple. All you have <strong>to</strong> do is<br />
1. Insert six <strong>SugarCRM</strong> specific lines in<strong>to</strong> the php<strong>unit</strong> script (see listing 1, lines 5-15).<br />
Now all neccessary global variables are initialized and accessable.<br />
1 #! / usr / bin / php -d safe_mode = Off<br />
2 < php<br />
3 // change <strong>to</strong> the sugarcrm root direc<strong>to</strong>ry .<br />
4 // this depends on your direc<strong>to</strong>ry structure !!<br />
5 chdir (’../../../ ’);<br />
6 // initialize sugarcrm environment _before_ doing anything<br />
else<br />
7 define (’sugarEntry ’, TRUE );<br />
8 require_once (’include / utils . php ’);<br />
9 require_once (’include / <strong>modules</strong> . php ’);<br />
10 require_once (’include / entryPoint . php ’);<br />
11<br />
12 // change <strong>to</strong> the direc<strong>to</strong>ry containing your <strong>unit</strong> <strong>test</strong>s and the<br />
13 // php<strong>unit</strong> direc<strong>to</strong>ry if neccessary . this depends on your setup<br />
14 // and direc<strong>to</strong>ry structure .<br />
15 chdir (" include / UnitTests / PHP ");<br />
16<br />
17 /* here starts the original content of the php<strong>unit</strong> script<br />
18 * leave unchanged<br />
19 */<br />
20 require_once ’ PHPUnit / Util / Filter . php ’;<br />
21 PHPUnit_Util_Filter :: addFileToFilter ( __FILE__ , ’ PHPUNIT ’);<br />
22 require ’ PHPUnit / TextUI / Command . php ’;<br />
23 ><br />
Listing 1: You have <strong>to</strong> adjust the php<strong>unit</strong> script like <strong>to</strong> workaround the <strong>unit</strong> <strong>test</strong>ing<br />
problems.<br />
2. Disable the backup global variables feature of PHPUnit for your <strong>test</strong>case(s) by<br />
setting $backupGlobals (see listing 2).<br />
1 < php<br />
2 require_once (’PHPUnit / Framework . php ’);<br />
3<br />
4 class FirstTest extends PHPUnit_Framework_TestCase {<br />
2
References<br />
5 protected $backupGlobals = FALSE ;<br />
6 ...<br />
7 }<br />
8 ><br />
Listing 2: Declare the protected variable $backupGlobals and set it <strong>to</strong> FALSE in your<br />
<strong>test</strong>case.<br />
If you are through this list and disabled the backup of globals as shown in listing 2<br />
you should be able <strong>to</strong> write <strong>unit</strong> <strong>test</strong>s as usual for your <strong>SugarCRM</strong> <strong>modules</strong> (see listing<br />
3 for an example).<br />
1 < php<br />
2 require_once (’PHPUnit / Framework . php ’);<br />
3<br />
4 class FirstTest extends PHPUnit_Framework_TestCase {<br />
5 protected $backupGlobals = FALSE ;<br />
6<br />
7 public function <strong>test</strong>Something () {<br />
8 chdir (’../../../../ ’);<br />
9 require_once (’<strong>modules</strong> / Users / User . php ’);<br />
10<br />
11 $u = new User ();<br />
12 $list = $u -> get_list (’’, ’’);<br />
13 $userList = $list [’list ’];<br />
14<br />
15 foreach ( $userList as $user ) {<br />
16 $this -> assertNotNull ( $user );<br />
17 $this -> assertFalse ( empty ( $user ->id));<br />
18 }<br />
19 }<br />
20 }<br />
21 ><br />
Listing 3: A basic <strong>unit</strong> <strong>test</strong> that can be executed with a modified version of your php<strong>unit</strong><br />
script.<br />
References<br />
[1] Php<strong>unit</strong>. http://www.php<strong>unit</strong>.de/.<br />
[2] Simple<strong>test</strong>. http://www.simple<strong>test</strong>.org/.<br />
[3] Objects disappear from the global scope.<br />
http://bugs.php.net/bug.phpid=40604.<br />
3
References<br />
[4] Inconsistent handling of $globals from included files.<br />
http://bugs.php.net/bug.phpid=34689.<br />
[5] Php manual entry about global statement. http://www.php.net/global.<br />
[6] Php<strong>unit</strong> and global variables. http://www.joelhainley.com/index.php/2008/<br />
04/30/php<strong>unit</strong>-and-global-variables/.<br />
[7] Global variables and php<strong>unit</strong>. http://sebastian-bergmann.de/archives/<br />
797-Global-Variables-and-PHPUnit.html.<br />
4