Kategorien
PHP PHPUnit Testing Webdeveloper Tools

Run PHPUnit Unit and Integrations test with different configurations in PHPStorm

If you want to develop unit and integration tests with PHPStorm you can easily bootstrap your application and run your tests. But if you want to develop real unit tests in your local development enviroment without having a database connection or a cache, you need to make sure, that your application is configured differently, when unit tests are running. In this article you will learn how to do that and how to make PHPStorm will automatically recognize which tests/tests suites you want to execute.

Requirement:

Your integration and unit tests are seperated in 2 directories, like:

/tests/unitTests
/tests/integrationsTests

Overview

There are 2 possibilities to configure PHPUnit to use different configurations:

First Option: Multiple phpunit.xml files

You can put 2 phpunit XML configuration files named integrationsTests.xml and unitTests.xml in your document root or in the folders /tests/unitTests or /tests/integrationsTests. The configurations files would look like this, with different bootstrap.php files, where you can load your configuration accodingly.

<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:noNamespaceSchemaLocation="phpunit.xsd"
       bootstrap="/tests/unitTests/bootstrap.php">
   <testsuites>
      <testsuite name="unitTests">
         <directory>/tests/unitTests</directory>
      </testsuite>
   </testsuites>
</phpunit>

The probnlem with this approach is, that PHPStorm is not able to automatically load the correct configuration file when you want to execute only one test class from e.g. the unit tests. You can only set one default configuration for all unit tests (PHPStorm: Run -> Edit Configuration-> Defaults -> PHPUnit)

phpsotrm_phpunit_default
PHPStorm default PHPUnit configuration

So when you execute a test file by right clicking on the test file or on the the test code and choose „Run“, than the unit test configuration is always executed.

So this option is not working with PHPStorm.

Second Option: Using a PHPUnit_Framework_BaseTestListener

This works with a single phpunit.xml, like this one

<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:noNamespaceSchemaLocation="phpunit.xsd"
       bootstrap="/tests/bootstrap.php">
   <testsuites>
      <testsuite name="unitTests">
         <directory>/tests/unitTests</directory>
      </testsuite>
      <testsuite name="integrationTests">
         <directory>/tests/integrationTests</directory>
      </testsuite>
   </testsuites>
   <listeners>
      <listener  class="TestListener" file="/tests/TestListener.php"></listener>
   </listeners>
</phpunit>

And TestListener like this:

<?php

class TestListener extends PHPUnit_Framework_BaseTestListener {

   const UNIT_TEST_DIR_NAME = 'unitTests';
   const INTEGRATION_TEST_DIR_NAME = 'integrationTests';
   /**
    * @var bool whether to the configuration was already loaded, performance optimization
    */
   private static $configurationLoaded = false;

   /**
    * startTestSuite
    *
    * @param PHPUnit_Framework_TestSuite $suite
    * @return void
    */
   public function startTestSuite(PHPUnit_Framework_TestSuite $suite) {
      if(self::$configurationLoaded === false){
         //the suite is only set, when the whole suite is executed, not when a test of a suite is executed
         switch ($suite->getName()){
            case 'unitTests':{
               $this->loadUnitTestConfiguration();
               break;
            }
            case 'integrationTests':{
               $this->loadIntegrationTestConfiguration();
               break;
            }
            default:{
               $this->loadConfigurationByFilePath();
               break;
            }
         }
         self::$configurationLoaded = true;
      }
   }

   /**
    * loadConfigurationByFilePath 
    * 
    * @throws Exception
    * @return boolean
    */
   private function loadConfigurationByFilePath(){
      if($this->isTriggeredByIDE() && $this->isRunningOnCLI()){
         for($i = $_SERVER['argc']; $i > 0; $i--){
            $arg = strtolower($_SERVER['argv'][$i - 1]);
            if(stristr($arg, DIRECTORY_SEPARATOR . self::UNIT_TEST_DIR_NAME . DIRECTORY_SEPARATOR)){
               $this->loadUnitTestConfiguration();
               return true;
            }
            if(stristr($arg, DIRECTORY_SEPARATOR . self::INTEGRATION_TEST_DIR_NAME . DIRECTORY_SEPARATOR)){
               $this->loadIntegrationTestConfiguration();
               return true;
            }
         }
      }
   }

   /**
    * isTriggeredByIDE - looks for PHPStorm VAR
    *
    * @return boolean
    */
   private function isTriggeredByIDE(){
      return isset($_SERVER['IDE_PHPUNIT_PHPUNIT_PHAR']);
   }

   /**
    * isRunningOnCLI
    *
    * @return boolean
    */
   private function isRunningOnCLI(){
      return isset($_SERVER['argc']) && isset($_SERVER['argv']);
   }

   /**
    * loadUnitTestConfiguration
    *
    * @return void
    */
   private function loadUnitTestConfiguration(){
      //Stub, implement, if you like
   }

   /**
    * loadIntegrationTestConfiguration
    *
    * @return void
    */
   private function loadIntegrationTestConfiguration(){
      //Stub, implement, if you like
   }
}

This Listener is executed before every testsuite execution and can trigger a configuration to be loaded when a integration or a unit test is executed. With the help of this listener you can execute tests in PHPstorm with just on phpunit.xml file and have different unit test configurations.

You can use the /tests/bootstrap.php file to execute configrations which are the same and put different configurations in the methods loadUnitTestConfiguration and loadIntegrationTestConfiguration.