Me Like Dev

Follow me on TwitterRSS Feeds

  • Home
  • About

PHP – PHPUnit – Use PHPUnit Without PEAR

Jan 25th

Posted by Mike Purcell in PHP

2 comments

Abstract

PHPUnit is a great tool to protect us developers from introducing new defects when adding new features or re-factoring code. However there is one HUGE downside to PHPUnit; it must be installed using PEAR. Personally, I don’t like ‘auto-installers’, I’d prefer to know what exactly is happening behind the scenes with regards to which libraries are required and how they are being called. So when I needed to use PHPUnit on a recent project I shed a tear thinking I would have to break down and install using PEAR.

PHPUnit is not a mythical creature, it doesn’t have magical powers, and as such, it should not intimidate us. It is PHP code, plain and simple, and like any other PHP API Libraries it can be interwoven into our application. So after breaking down the PHPUnit source code, I realized it could be installed without going through PEAR, and without too much headache.

I wrote this article with the goal that it may help others, and maybe even make it’s way to the lead developer of PHPUnit, where he may start using a better autoloading strategy. However, I must warn you to not hold me responsible for any “issues” that may arise on your system by following this article, as it’s strictly demonstrative at this point. There may come a time where I add this code to a GIT repo and officially support it, but not at this point. If you are ok with that, then read on.

Also, note that you can change any of the following guidelines to suit your specific needs. What I am outlining here worked for me, but may not work for you, so feel free to change what you need to, in order to accomplish your end game.

Base Directories

To make things work, we need a base directories from which to work. As with any setting throughout this article, you can change to meet your needs.

Test directory

This directory will be where we put our actual unit tests:

/home/mpurcell/projects/core/test

Vendor directory

This directory will be the location where we download and extract PHPUnit source code:

/home/mpurcell/projects/vendor

Source Code:

Now that we have our directories in place, lets get PHPUnit source code. Normally you would use PEAR to download, extract, and prepare the source code, but personally I don’t like things of this nature being done for me, I am a big boy, I can handle it myself. So I download each of the core packages manually, then extract them, and set their correct directories in preparation for the next ‘Symlinks‘ step.

The PHPUnit developer uses Github as their version control repo, which is good, but sucks when trying to download packages, as the actual files are abstracted by an intermediary PHP script. What I ended up having to do was download to my local machine, then secure copy them over to the server. Once you have the source files on the server, run the following steps:


$ -> cd ~/projects/vendor

$ -> mkdir phpunit

$ -> cd phpunit

$ -> cp ~/sebastianbergmann-phpunit-3.6.7-9-gf5e159b.zip .

$ -> unzip sebastianbergmann-phpunit-3.6.7-9-gf5e159b.zip

# The unzip will result in a weird hash, so I just rename it to
# the correct version based on the original file name
$ -> mv sebastianbergmann-phpunit-f5e159b/ 3.6.7-9

# Don't need source file anymore
$ -> rm *.zip

# Now, we have phpunit locked into a version which we can quickly
# glean when we inspect symlinks to this library
# Lets get the other necessary packages
$ -> mkdir lib

$ -> cd lib
$ -> pwd
/home/mpurcell/projects/vendor/phpunit/lib

# Setup base dirs for phpunit add-ons
$ -> mkdir codeCoverage fileIterator timer

# Now lets copy our add-ons into their respective dirs
$ -> cp ~/sebastianbergmann-php-code-coverage-1.1.1-14-gdc2a15a.zip codeCoverage
$ -> cp ~/sebastianbergmann-php-timer-1.0.2-5-gb352e92.zip timer

# File iterator lib allows you to set a root directory, and phpunit will traverse the directory looking for unit tests
$ -> cp ~/sebastianbergmann-php-file-iterator-1.3.1-1-gbbaab46.zip fileIterator

# Now that we have the files in our lib directory, lets get them setup
$ -> cd codeCoverage
$ -> pwd
/home/mpurcell/projects/vendor/phpunit/lib/codeCoverage

$ -> unzip sebastianbergmann-php-code-coverage-1.1.1-14-gdc2a15a.zip
$ -> mv sebastianbergmann-php-code-coverage-dc2a15a/ 1.1.1-14
$ -> rm *.zip

$ -> cd ../fileIterator
$ -> pwd
/home/mpurcell/projects/vendor/phpunit/lib/codeCoverage

$ -> unzip sebastianbergmann-php-file-iterator-1.3.1-1-gbbaab46.zip
$ -> mv sebastianbergmann-php-file-iterator-bbaab46/ 1.3.1-1
$ -> rm *.zip

$ -> cd ../timer
$ -> pwd
/home/mpurcell/projects/vendor/phpunit/lib/codeCoverage

$ -> unzip sebastianbergmann-php-timer-1.0.2-5-gb352e92.zip
$ -> mv sebastianbergmann-php-timer-b352e92/ 1.0.2-5
$ -> rm *.zip

Ok, now you should have phpunit and it’s core libraries “installed” on your server. In this context, installed is a loose term, as the code isn’t really installed, it’s now available to be hooked into from your testing environment.

Symlinks

As with any 3rd party APIs introduced into your application, it is always best to abstract away version numbers, so you don’t force your application to require files with version numbers in a file’s uri. For example:

// Good
require_once 'path/to/ThirdParty_Vendor/Api/Class.php';

// Bad
require_once 'path/to/ThirdParty_Vendor-1.1.1-9/Api/Class.php';

Why does this matter? Because, when a new version of the file is released, and you upgrade, you won’t have to update all the code references. Instead, just change the related symlink and your application will work as before, except with the newer version of the file.

So lets make the PHPUnit libraries available to our testing environment:

$ -> cd ~/projects/core/test
$ -> mkdir lib
$ -> cd lib
$ -> pwd
/home/mpurcell/projects/test/lib

$ -> ln -s ~/projects/vendor/phpunit/3.6.7-9/PHPUnit
$ -> ln -s ~/projects/vendor/phpunit/lib/codeCoverage/1.1.1-14/PHP PHPCodeCoverage
$ -> ln -s ~/projects/vendor/phpunit/lib/fileIterator/1.3.1-1/File PHPFileIterator
$ -> ln -s ~/projects/vendor/phpunit/lib/timer/1.0.2-5/PHP PHPTimer

Ok, now we have hooks to phpunit libraries within our testing directory, onto the next step.

Bootstrap

A bootstrapper is meant to prepare a library for usage, and that’s exactly what we are going to do. This bootstrapper will setup include_paths along with a few other settings so we can use run our tests correctly. The code is posted here, otherwise it’s too hard to read in wordpress.

Autoloader

Here’s where the magic lies. This autoloader will know how to map files that PHPUnit is requiring, to their symlinked location. It’s pretty easy to follow, all we do is check for a namespace footprint (PHP_Timer), and if it exists, re-map it to our symlink (PHPTimer). The code is posted here, otherwise it’s too hard to read in wordpress.

Changes to PHPUnit Source code

Now for the ugly. It is NEVER a good idea to edit 3rd party (vendor) code directly, otherwise you update and forget about the changes and your app goes to hell. However in this instance I had no choice, and fortunately I was able to limit the change to just one location in the PHPUnit source code. It is a trivial change, but you should add a README to your ~/projects/vendor/phpunit directory to remind yourself to make this change for future upgrades (assuming the lead developer doesn’t change it himself).

My README file:

ATTENTION!
When upgrading phpunit, 1 change must be made to ensure unit tests work

Why:    This allows us to use our own autloader
File:   PHPUnit/Util/GlobalState.php
Line:   98
Change:
    From:    public static $phpunitFiles;
    To:      protected static $phpunitFiles;

This change makes it so we can override the singleton $phpunitfiles. If we don’t over-ride it, then PHPUnit will attempt to use it’s built-in autoloader, which doesn’t work with our setup. By setting the scope from protected to public, we can over-ride the value with an empty array, as the conditional used in source code check is ‘ === NULL’.

phpunit executable

Now we need to create the phpunit executable which will kick off the unit testing. When I was going through the PHPUnit source code, I noticed that the PEAR installer was just renaming the phpunit.php to phpunit. So, lets copy the phpunit executable which came with PHPUnit to our test directory and make a few minor changes:

$ -> pwd
/home/mpurcell/projects/core/test

$ -> cp ~/projects/vendor/phpunit/3.6.7-9/phpunit.php .

Now, make the following changes:

#!/usr/bin/env php

// Bring in our bootstrap file
require_once substr(__FILE__, 0, strpos(__FILE__, '/test')) . '/test/Bootstrap.php';

// Set our main define
define('PHPUnit_MAIN_METHOD', 'PHPUnit_TextUI_Command::main');

// Remember we set the scope of $phpunitfiles to public?
// Now we can set it to whatever we want, which is just an empty
// array, which allows our autoloader to handle loading files
PHPUnit_Util_GlobalState::$phpunitFiles = array();

// Kick this pig
PHPUnit_TextUI_Command::main();

Home Stretch

Finally, we made it. Now, we can call on the phpunit executable and run tests. First lets create a sample test:

$ -> pwd
/home/mpurcell/projects/core/test

$ -> touch CoreTest/ArrayTest.php

Now lets add an easy test:

<?php
class CoreTest_Api_Array_PackageTest extends PHPUnit_Framework_TestCase{
     public function testCount()
     {
         $this->assertCount(2, array('foo', 'bar'));
     }
}

Now, run the test:

$ -> pwd
/home/mpurcell/projects/core/test

$ -> ./phpunit/CoreTest/

Now your ArrayTest test should run (and pass). Notice that we passed a directory to the ./phpunit executable, you can pass a directory, or a specific test to meet your needs. The PHPFileIterator we setup will parse directories for any files ending in *Test.

Congratulations

Hopefully this article wasn’t too hard to follow, and your unit tests are running though PHPUnit, as if you installed through PEAR. If you wouldn’t mind taking the time to contact the lead developer, and let him know that he should drop PEAR and run an autoloader/bootstrap combo like many other PHP libraries/frameworks, I would greatly appreciate it. He has done a good job, but we need to make PHPUnit easier to install and set-up so others don’t get discouraged and give up unit tests altogether.

pear, PHP, phpunit

General – Merry Christmas – 2011

Dec 24th

Posted by Mike Purcell in General

No comments

Just wanted to thank everyone who has supported melikedev.com over the last year. I’d like to wish everyone a Merry Christmas and Happy New year, and we look forward to posting more great articles in 2012.

General

Linux – CentOS – GIT – Version Mismatch – CentOS5 CentOS6

Dec 21st

Posted by Mike Purcell in Linux

No comments

Be careful when upgrading CentOS 5.x servers to 6.x with respect to GIT. On CentOS 5.x GIT was not available via default repo, so I compiled it manually and it was on version 1.7.6, but when I installed CentOS 6 and did a yum install for GIT, it was at version 1.7.1.

Glad that CentOS finally provided GIT via the default repo, I let yum handle the install, but when I went to start pushing code to my production box (with git 1.7.6) I kept getting GIT failures. Only after I upgraded my production box to CentOS6 and yum installed GIT, did my code push script work again.

CentOS, Linux

Linux – PHP – Apache – HTTP Error 500

Dec 21st

Posted by Mike Purcell in Apache

No comments

Had a weird issue where my demo server was throwing 500 error when a request was made. I spent time digging into my nginx configs to see if there were a issue, once I was able to determine it was not nginx, I started tearing apart my apache vhosts to see what the issue was. It was tough to track down because neither nginx nor apache error logs were logging anything out of the ordinary.

I came across a serverfault.com post where someone suggested using the following command after making a request:

find /var/log/ -mmin -1

This command will return any files whose modtimes are less than a minute old. When I issued the command I noticed that my PHP error log was listed, so I tailed the error log and issued another request. Sure enough, an exception was being thrown because my bootstrap file for my core library could not make a needed database connection.

Now that I was aware that an exception was causing my apache server to issue a 500 response, I still couldn’t figure out why it wasn’t outputting the exception message. I started digging around my php.ini file and found that I had set `display_errors=off`. With display_errors set to off, apache will issue a 500 response rather than output the exception, which is a good thing b/c the exception message had some database connection information.

So if you are setting up an apache/php server and it’s throwing a 500 response, check the php error log too, you may have the same setup.

Also, I read that this type of behavior will occur if the php.ini file could not be read.

Apache, Linux, PHP

Linux – CentOS6 – Git – fatal – Where do you want to fetch from today?

Dec 9th

Posted by Mike Purcell in Git

No comments

If you are using GIT as your version control and you attempt to do a `git pull` and get a “fatal: Where do you want to fetch from today?” message, you need to do either of the following:

# Specify the remote repo
mkdir repo
cd repo && git init
git pull git@github.com:user/repo.git

# Or
# Clone the repo
mkdir repo
cd repo && git clone git pull git@github.com:user/repo.git .
git pull
Git, Linux

Linux – CentOS6 – httpd – mod_file_cache – mod_mem_cache – mod_imagemap – Cannot Load into Server – Cannot Open Shared Object

Dec 9th

Posted by Mike Purcell in Linux

No comments

If you are upgrading from CentOS5 to CentOS6 and attempt to start httpd, you may come across a message similar to: “Starting httpd: httpd: Syntax error on line 196 of /etc/httpd/conf/httpd.conf: Cannot load /etc/httpd/modules/mod_file_cache.so into server: /etc/httpd/modules/mod_file_cache.so: cannot open shared object file: No such file or directory”. Apparently the removal of some apache mods was an upstream decision which CentOS passed through to us. You can find more information here.

The quick fix is to remove these references from your httpd.conf file, however if you depend on these modules being available, you will have to download and compile manually.

CentOS, httpd, Linux

Linux – CentOS – SELinux – Cannot Start httpd – Permission to httpd.conf Denied

Dec 9th

Posted by Mike Purcell in Linux

No comments

If you are attempting to start apache (httpd) and get permission denied errors, chances are your SELinux is enabled, and not configured to allow httpd connections. Use the following commands to get your httpd working.

# To view current selinux settings related to httpd:
getsebool -a | fgrep -i httpd

# To "pinhole" SELinux to allow httpd to start correctly:
setsebool -P httpd_can_network_connect 1
setsebool -P httpd_enable_homedirs 1
CentOS, Linux, selinux

Linux – Ruby – Capistrano – GIT – No Such File or Directory – Errno::ENOENT

Dec 8th

Posted by Mike Purcell in Linux

No comments

If you are running capistrano to execute commands on another server and get a message that looks like:

No such file or directory - /usr/local/git/bin/git ls-remote git@github.com:user/repo.git master (Errno::ENOENT)

Be sure the user whom you are logging in as through capistrano has access to `git`. To test whether the user has access, ssh as the user and type `git`, if you get a bash error, then set the path in the user’s ~/.bash_profile. Otherwise you should see a list of git options.

capistrano, Git, Linux, Ruby

Linux – CentOS6 – Ruby – It seems your ruby installation is missing psych

Dec 7th

Posted by Mike Purcell in Linux

No comments

If you are compiling ruby from source on a CentOS box, you may come across a “it seems your ruby installation is missing psych” message. To fix this issue you will also need to compile libyaml. If you installed libyaml to a custom directory you will need to let ruby know during configuration:

 ./configure --prefix=/usr/local/_custom/app/ruby --with-opt-dir=/usr/local/_custom/app/libyaml/

Now, after you have done `make && make install` you should be able to run ruby without the error message.

CentOS, Linux, Ruby

Linux – CentOS6 – Adding or Updating Custom Gateway

Dec 7th

Posted by Mike Purcell in Linux

No comments

Just started working with CentOS6 and came across an issue where I found it difficult to add a gateway for my eth0 interface. The netinstall I conducted didn’t appear to have an entry area for it (or I may have missed it). If you need to add or update your gateway do the following:

vi /etc/sysconfig/network
NETWORKING=yes
GATEWAY=192.168.0.254
/etc/init.d/network restart

Now your gateway should be working as expected.

CentOS, Linux
12345»10...Last »
    • Recent comments
    • Popular posts
    • Archives
    • Categories
    • Ajax (1)
    • Apache (3)
    • Browser (2)
    • Debugging (1)
    • Doctrine (7)
    • Fun (5)
    • General (5)
    • IDE (1)
    • Javascript (3)
    • JQuery (6)
    • Linux (24)
    • MAC (4)
    • MemCache (1)
    • MySQL (16)
    • News (10)
    • PHP (14)
    • PHPUnit (1)
    • Propel (2)
    • Ruby (2)
    • Setup Guides (3)
    • Solr (2)
    • Subversion (4)
    • Symfony (36)
      • Symfony 1.4 (4)
    • Uncategorized (1)
    • Version Control (2)
      • Git (2)
    • Windows (1)
    • Wordpress (1)
    • January 2012 (1)
    • December 2011 (10)
    • November 2011 (1)
    • October 2011 (7)
    • September 2011 (1)
    • August 2011 (5)
    • July 2011 (6)
    • June 2011 (3)
    • May 2011 (3)
    • April 2011 (1)
    • March 2011 (2)
    • January 2011 (2)
    • December 2010 (2)
    • November 2010 (2)
    • October 2010 (1)
    • September 2010 (1)
    • August 2010 (2)
    • July 2010 (6)
    • June 2010 (3)
    • May 2010 (1)
    • April 2010 (5)
    • March 2010 (6)
    • February 2010 (8)
    • January 2010 (5)
    • December 2009 (16)
    • November 2009 (8)
    • October 2009 (24)
    • September 2009 (1)
    • Symfony – Doctrine – Saving Many-To-Many (M:M) relationships (72)
    • Zend Studio 5.5 on Windows 7 (27)
    • Symfony – Saving Metadata During Form Save (sort ids) (25)
    • Symfony – Autoload.yml – Basic Usage (21)
    • Symfony – PHP – Possibly Forking 1.x so Invested Companies don’t Lose Millions (11)
    • PHP – Wrap Implode Array Elements in Quotes (7)
    • Symfony – Adjust Widget Order (7)
    • Symfony – sfGuardPlugin – Use Email Instead of Username (6)
    • WordPress – Syntax Highlighter – Add Custom Brush(es) (5)
    • Symfony – Standard API for Logging (5)
    • Mike Purcell: @Edorian: Nice, let me know when you get phpunit.phar going, I'd like to check it out. I tried...
    • Edorian: We are working on a phpunit.phar so you don't have to do anything anymore to get to run except...
    • Nuramon: Thanks man, I love you...great installation guide, for still one of the best IDEs in the world!...
    • Sune: Thank you very much! Been through a lot of posts on how to change TZ, none of them had any...
    • DavidE: Thanks a million worked like a charm!!
    • Egle Samuleviciute: Dear Mike, I just would like to thank you for this great article. I have waisted days trying to...
    • Mike Purcell: @Lawrence: Thanks again for you post several months back, just ran into a similar situation and you...
    • Mike Purcell: @Erik: Need a little more information in order to help.
  • Translator

  • Tags

    Ack Ajax Apache Apple browser CentOS Doctrine Firebug Firefox Fun General Git Google Hydration IDE internet explorer Javascript JQuery Linux linux bash dos2unix MAC MAMP MemCache MySQL News ORM Percona PHP php facebook Propel Ruby Serialize sfguardplugin Solr SSH Subversion Symfony Telnet Timestamp Windows Wordpress XAMPP yaml YUM Zend
  • User Login






    • Lost your password?
  • Development Links

    • Craig Godfrey – UI Professional
    • Roy Laurie – Software Engineer
    • Strikeforce Media
Mystique theme by digitalnature | Powered by WordPress
RSS Feeds XHTML 1.1 Top