Visualizzazione post con etichetta php. Mostra tutti i post
Visualizzazione post con etichetta php. Mostra tutti i post

mercoledì 21 settembre 2011

Extremely Simple PHP Unit Test Framework/3 [ENG]

Part I

Part II

We've seen the test suite, the test, the test runner (the contraption required to get the test to run). The last thing is the test base class which implements the assertions.

class Test
{

 function assertEquals($expected, $got)
 {
  if ($expected!=$got)
  {
   trigger_error("Expected '" .$expected. "' but got '" .$got. "'", E_USER_ERROR);
  }
 }

 function assertContains($haystack, $needle)
 {
  if (strpos($haystack, $needle)<0)
  {
   trigger_error("String '" .$needle. "' is not contained in '" .$haystack. "'", E_USER_ERROR);
  }
 }
 
 function assertNotNull($object)
 {
  if ($object==NULL)
  {
   trigger_error("assertNotNull failure", E_USER_ERROR);
  }
 }
 
 function assertTrue($condition)
 {
  if (!$condition)
  {
   trigger_error("assertTrue failed", E_USER_ERROR);
  }
 } 
 
}

As you can see I kept that really really simple.

Extremely Simple PHP Unit Test Framework/2 [ENG]

How does ESP-UTF (see part 1) run the single test?

function doTest($testClassName, $testMethodName)
{
 global $testFailed;
 global $testFailedCause;
 $testFailedCause = "";
 $testFailed = false;
 
 $object = new $testClassName();
 $object->$testMethodName();
 
 $result = new TestResult($testClassName, $testMethodName, !$testFailed, $testFailedCause);
 
 return $result;
}

Things to note here:

  • the test result is held by a pair of global variables (lines 3 and 4); that's how I handled the errors; more on that later
  • strings can be used as class and methods names without further ado (lines 8 and 9)
  • the result is returned as a TestResult object

I did not know how to trap errors (did I already say that I'm stuck with PHP4 and no exception handling?) other than by setting an error handler:

set_error_handler ('errorHandler');

function errorHandler($errno, $errstr, $errfile, $errline)
{
 global $testFailed;
 global $testFailedCause;
 $testFailedCause = "\nError: " . $errno . ", file: " . $errfile . "; line: " . $errline . "; error: ". $errstr . "";
 $testFailed = true;
 return true;
}

There's catch of course: if an assertion fails the execution of the method does not get stopped, it just goes on through to the end because that's how the error handler's supposed to work. Since I consider that a minor annoyance I did not waste further effort trying to mend that.

Here's the TestResult class:

class TestResult
{
 var $testClassName;
 var $testMethodName;
 var $testBooleanResult;
 var $testMessageResult;
 
 function TestResult($className, $methodName, $booleanResult, $messageResult)
 {
  $this->testClassName = $className;
  $this->testMethodName = $methodName;
  $this->testBooleanResult = $booleanResult;
  $this->testMessageResult = $messageResult;
 }
 
 function x()
 {
  return $this->testClassName . "." . $this->testClassMethod . "='" . $this->testMessageResult . "'";
 }
 
}

Nothing fancy, in fact.

In the next episode the Test class

lunedì 19 settembre 2011

Extremely Simple PHP Unit Test Framework [ENG]

Ok, I know. There is PHPUnit, already, and enough good books to get you started if you want to go down that path (that is the sensible thing to do, by the way).

Since I had to play with PHP for a pet project, got stuck with PHP4, was not able to set up PHPUnit and lost my patience, I decided to whip up something on my own. ESP-UTF is just an experiment: I wanted to peek behind the curtains to see how a unit test framework is made. Hope you may find something interesting among this crap.

First of all I only knew how to send the output of PHP scripts to a web page, so that's how I thought to show the test outcome.

How will the test cases look like? Well something like this:

class TestLanguage extends Test
{

 function testLanguageUK()
 {
  $pLang = new phpI18N('en');
   $testResult = $pLang->getPhrase('register', 'first_name');
  $this->assertEquals('First Name', $testResult);
 }

}

As in JUnit 3 I need to extend a basic Test class, and the test method has to be called testName. Since that's exactly what you have to do in Groovy I can live with that.

Now, how do I create a test runner? I did not want to do anything complicated, just a simple report. And I wanted my framework to handle test suites. Here's how I did that:

$testSuite = array(
 "TestDAOArticoli",
 "TestDAOUtente",
 "TestDAOCategoria",
 "TestLanguage",
 "TestCart"
);

$testResults = doTestSuite($testSuite);

render($testResults);

Could there be anything simpler? How do I load the test methods from each class?

function doTestSuite($testSuite)
{
 $testResults = array();
 foreach($testSuite as $testClass)
 {
  $testResults = array_merge($testResults, doTests($testClass));
 }
 return $testResults;
}

I collect the test results in an array. The render function will display that in a fancy style. Here's the doTests() method (more interesting):

function doTests($testClassName)
{
 $testResults = array();
 $class_methods = get_class_methods($testClassName);
 foreach ($class_methods as $method_name) 
 {
     if (preg_match("/^test/", $method_name))
     {
          $testResults[] = doTest($testClassName, $method_name);
     }
 }
 return $testResults;
}

No rocket science, so far, right? In the next episode: how to do the test?