Optimize website unit testing with phpUnit

Optimize website unit testing with phpUnit

Writing code is only the first step in creating an enterprise website. Organizations also need to test their sites, and that can take a lot of financial and human resources. Optimizing the process with a tool like phpUnit and performing regular tests after major modifications saves time and money and helps avoid issues with the final product.

In some organizations software engineers still perform manual quality assurance (QA) checks, such as clicks on different website items and the inclusion of a huge number of debugging patterns in the application’s source code. To improve an organization’s efficiency, however, developers can write small testing modules called units and run unit tests to ensure that a website’s functional elements complete the expected sets of actions properly. The idea is to automate testing of your applications’ functions and make sure that modifications work before the launch date, instead of debugging them afterward.

Writing unit tests may seem like a difficult and time-consuming task, but it will save you a lot of effort and resources throughout the development and launch process. Also, unit tests can be used as documentation of an application’s functions and their expected behavior.

PhpUnit is a popular solution for testing websites’ functional units. Let’s see how to write a simple phpUnit acceptance test, and also see how to run phpUnit tests for an existing application, using WordPress as an example.

PhpUnit requires PHP 5.3.3 and above. Use the latest available PHP release to make sure that you can use all recent features, and that bugs discovered in previous versions are fixed. You can check the installed PHP version by executing php -v at the command line. Run yum upgrade php on your CentOS server to upgrade. You should also install the php-xml package – yum install php-xml – for WordPress unit tests.

You can install phpUnit either through phar (PHP archive) or through Composer. For phar, use the following commands:

wget https://phar.phpunit.de/phpunit.phar
 chmod 755 phpunit.phar
 mv phpunit.phar /usr/local/bin/phpunit
 phpunit --version
 PHPUnit 4.1.0 by Sebastian Bergmann.

Installation with the Composer PHP dependency manager is more complicated. You must first install Composer locally for your project or globally for the server, as explained in Composer’s documentation. In your command line interface type the command export PATH=~/.composer/vendor/bin:$ PATH to add Composer’s directory to your environment $ PATH variable. Once you have Composer on your system you can run the command composer global require 'phpunit/phpunit=4.1.*' to complete the global installation of the latest phpUnit release available today.

Writing tests with phpUnit

To see how phpUnit works, let’s start with a simple test case that checks whether a remote server URL supports SSL connections. To do this, create a simple PHP class that establishes a remote connection through port 443, the default port for SSL:

<?php
 class RemoteSSLConnection
 {
   public function connectToServer($ serverName=null)
   {
     if($ serverName==null){
       throw new Exception("Missing or incorrect server name!");
     }
     $ fp = fsockopen($ serverName,443);
     return ($ fp);
   }
 }
 ?>

This code declares a new class called RemoteSSLConnection and a public method that tries to connect to a remote server on the default SSL port through the fsockopen PHP function.

Next, create a unit test that uses this class to check whether the remote URL can be loaded through SSL:

<?php
 require_once('remoteconnection.php');
 class RemoteSSLConnectonTest extends PHPUnit_Framework_TestCase
 {
   public function testConnectionIsTrue()
   {
     $ connectionObj = new RemoteSSLConnection();
     $ serverName = "ssl://google.com";
     $ this->assertTrue($ connectionObj->connectToServer($ serverName) !== false);
   }
 }
 ?>

In this code the require_once() statement includes the code we want to test in the actual test unit. A new test class extends the default PHPUnit_Framework_TestCase class. As you do this, bear in mind some rules and recommendations for writing test units. The method of the class should be public in order to execute the test through phpUnit. The test class name should match the name of the original class (in the case RemoteSSLConnection) that you test, with an additional “Test” string at the end. Test method names start with the “test” string and do not receive parameters. Here, the testConnectionIsTrue code creates a new object from the original class you want to test. The next line defines the server name variable. In the last line the assertion method assertTrue() verifies whether you can establish the actual connection to the chosen hostname through port 443 as expected.

You can now run the test:

phpunit remoteconnectiontest.php
 PHPUnit 4.1.0 by Sebastian Bergmann.
 .
 Time: 81 ms, Memory: 2.75Mb
 OK (1 test, 1 assertion)

The expected functionality is confirmed. If you change the server name for the test to one that does not accept HTTPS requests the test will fail with an error:

phpunit remoteconnectiontest.php
 PHPUnit 4.1.0 by Sebastian Bergmann.
 
 E
 
 Time: 253 ms, Memory: 2.75Mb
 
 There was 1 error:
 
 1) RemoteSSLConnectonTest::testConnectionIsTrue
 fsockopen(): unable to connect to ssl://mytest.com:443 (Connection refused)
 
 /root/remoteconnection.php:11
 /root/remoteconnectiontest.php:13
 
 FAILURES!
 Tests: 1, Assertions: 0, Errors: 1.

The full list of assertion methods is available in the phpUnit 4.1 documentation.

Extend phpUnit

Most of the popular PHP frameworks have additional sets of tools that allow you to extend the core phpUnit functionality. So does WordPress. You can download the WordPress unit test code base with the command svn checkout http://unit-tests.svn.wordpress.org/trunk wordpress-tests.

Once you’ve downloaded the WordPress test suite, navigate to its location (your_wordpress_folder/wordpress-tests)and follow the instructions in the README.txt file. You need to create a new database for your tests, copy the wp-tests-config-sample.php file to wp-tests-config.php, and enter the new database details in the file. Also update the latest changes from the remote svn repository to your project’s working copy:

mysql
 mysql> create database wp_test;
 mysql> grant all privileges on wp_test.* to [email protected] identified by [email protected]';
 cp wp-tests-config-sample.php wp-tests-config.php
 vi wp-tests-config.php
 svn up

If you want to, you can run all the core WordPress tests with the phpunit command, or you can pick a specific test from the tests subfolder. During the first attempt you might see an error similar to PHP Fatal error: require_once(): Failed opening required 'PHPUnit/Autoload.php' (include_path='.:/usr/share/pear:/usr/share/php') in /var/www/html/my_WP_site/wordpress-tests/includes/bootstrap.php on line 7. To keep this from happening, comment out a line you don’t need from the bootstrap.php file:

//require_once 'PHPUnit/Autoload.php';

You can then run unit tests on WordPress core URL functionality – for example:

phpunit tests/url.php
 Installing...
 Running as single site... To run multisite, use -c multisite.xml
 Not running ajax tests... To execute these, use --group ajax.
 PHPUnit 4.1.0 by Sebastian Bergmann.
 
 Configuration read from /var/www/html/my_WP_site/wordpress-tests/phpunit.xml.dist
 
 ...........
 
 Time: 3.1 seconds, Memory: 22.00Mb
 
 OK (11 tests, 165 assertions)

What if you want to create phpUnit tests for a WordPress plugin you’re developing? Let’s suppose you have a plugin called ServerInfo. It has a class that collects server information, such as the server host name and operating system. First, create the following folder structure under the wp-content/plugins/ directory:

ServerInfo/
 	tests/

Create your plugin.php file under the ServerInfo/ subdirectory, following the instructions on how to create WordPress plugins in the official documentation. Here I will omit the plugin’s header code and share only the source code of the actual PHP class that gathers the data that we want to test, which uses the standard php_uname function to return data for the server’s host name and OS:

class ServerInfo {
 	public function GetHostName() {
         $ hostname = php_uname("n");
         return $ hostname;
     }
     public function GetOS() {
 		$ os = php_uname("s");
 		return $ os;
     }
 } 

Now you can copy the downloaded WordPress unit tests into the test subfolder and proceed with the actual unit test code in server_info_test.php.

<?php
 
 require_once( '../plugin.php' );
 
 class ServerInfoTest extends WP_UnitTestCase {
     function testGetHostName() {
         $ Obj = new ServerInfo();
         $ expected_hostname = "mytestserver";
         $ this->assertEquals($ Obj->GetHostName(),$ expected_hostname);
     }
     function testGetOS() {
         $ Obj = new ServerInfo();
         $ expected_os = "Windows";
         $ this->assertEquals($ Obj->GetOS(),$ expected_os);
     }
 }
 ?>

WordPress has its own class for unit tests called WP_UnitTestCase, which is included in the WordPress tests suite that you downloaded.

As you might have guessed, the first test we created above passes while the second fails:

phpunit  server_info_test.php
 Running as single site... To run multisite, use -c multisite.xml
 Not running ajax tests... To execute these, use --group ajax.
 mytestserverLinuxPHPUnit 4.1.0 by Sebastian Bergmann.
 
 Configuration read from /var/www/html/my_WP_site/wp-content/plugins/ServerInfo/tests/phpunit.xml.dist
 
 .F
 
 Time: 68 ms, Memory: 21.25Mb
 
 There was 1 failure:
 
 1) ServerInfoTest::testGetOS
 Failed asserting that two strings are equal.
 --- Expected
 +++ Actual
 @@ @@
 -'Linux'
 +'Windows'
 
 /var/www/html/my_WP_site/wp-content/plugins/ServerInfo/tests/server_info_test.php:14
 
 FAILURES!

When you write your code, you expect certain results for each functionality. Always try to write unit tests that pass. To do that in this case you could either change the $ expected_os variable to Linux or change the test logic and replace the assertEquals() method with assertnotEquals().

phpunit  server_info_test.php
 Running as single site... To run multisite, use -c multisite.xml
 Not running ajax tests... To execute these, use --group ajax.
 mytestserverLinuxPHPUnit 4.1.0 by Sebastian Bergmann.
 
 Configuration read from /var/www/html/my_WP_site/wp-content/plugins/ServerInfo/tests/phpunit.xml.dist
 
 ..
 
 Time: 41 ms, Memory: 20.25Mb
 
 OK (2 tests, 2 assertions)

For best results, bear in mind the following rules when you write your unit tests:

    • Each test function you write should assert the output from only one functionality of your application. You should write separate tests for each possible condition.

 

  • Each test should be independent from the others you write. They should not exchange parameters.

 

 

  • You should be able to repeat the tests multiple times with changed entry values.

 

 

  • Design the tests to pass successfully, and use static data, since it makes it much easier to spot differences in the expected outcome.

 

 

Unit tests provide the necessarily quality control level that keeps your project and its updates as free of bugs and as reliable as possible. phpUnit is a great way to implement unit testing for website functionality.