i want to write tests for a quite large and complicated project (better later than never).
i made the "code" runnable via bootstrap and tests, but i have some problems with exit commands inside the project ...
i have a testclass like this
class website_call_direct_Test extends PHPUnit_Framework_TestCase
{
public function execute(array $req){
$_REQUEST = array();
error_reporting(E_ALL & ~E_DEPRECATED & ~E_NOTICE & ~E_STRICT);
foreach($req as $k => $v){
$_REQUEST[$k] = $v;
}
$_GET = &$_REQUEST;
$_POST = &$_REQUEST;
ob_start();
include(G::$baseDir ."/index.php");
$output = ob_get_clean();
return $output;
}
/**
* #runInSeparateProcess
*/
public function testPrecaution()
{
$req = array();
$req['ajaxreq'] = 1;
$req['m'] = "precaution";
$req['type'] = "list";
$req['mode'] = "default";
$req['wnd'] = "new";
$output = $this->execute($req);
echo $output;
//SOME validation with the output
$this->assertEquals(false, strpos("...", $output));
throw new Exception();
}
}
The Problem is that the Exception is never thrown because the script ends with an exit at several points. i know i can test some classes directly, but i want to ensure that some calls with some variables do not produce errors / exceptions. is there any workaround beside removing every exit in the project?
Looks like you're testing the whole app. I don't think there is any way of disabling the exit sentences.
The workaround (and recommended way of testing whole apps), is accesing the site using curl, and checking that the HTML contains what you expect.
Even better if you use a scraping solution that will let you query the HTML using selectors or XPath, like this one.
Related
For a long time I have a problem - should I reuse small parts of code and if so, how should I do it so it would be the best practice.
What I mean about small code is for example:
if (!is_array($table)) {
$table = array($table);
}
or
$x = explode("\n", $file_content);
$lines = array();
for ($i=0, $c = count($x); $i<$c; ++$i) {
$x[$i] = trim($x[$i]);
if ($x[$i] == '') {
continue;
}
$lines[] = $x[$i];
}
Such tiny parts of code may be used in many classes in one project but some of them are used also in many projects.
There are many possible solutions I think:
create simple function file and put them all reusable piece of codes as function, include them simple in project and use them whenever I want
create traits for those piece of codes and use them in classes
reuse code by simple copy paste or creating function in specific class (??)
other ?
I think all of those solutions have their pros and cons.
Question: What method should I use (if any) to reuse such code and why is this approach the best one in your opinion?
I think that "the best way" depends on many factors including the technology your applications use (procedural, OOP), versions of PHP they run on, etc. For example, traits are interesting and useful but they are available only since php 5.4.0 so using this tool to group your code snippets you will not be able to reuse them in systems running on earlier PHP versions. On the other hand if your app uses an OOP style and you organized your resuable small code snippets in functions, their usage may seem awkward in an OOP app and conflict with the function names in a particular class. In this case I think grouping your functions in classes would seem more natural.
Putting everything together, it seems that classes provide better tool for grouping resuable code snippets in terms outline above, namely backward compatibility with earlier PHP versions, avoiding function names conflicts, etc.) Personally I code mostly in OOP, so i have a Util class where I group small functions representing resuable pieces of code snippets that do not directly relate to each other and thus could not be logically groupped in other classes.
As mentioned already traits are a good thing. But might be a bit hard to manage after a while, and it might not be supported everywhere since its new.
What I do is to create Tool classes that have a lot small static functions, like:
class ArrayTools
{
static public function CheckArray($array)
{
if (!is_array($array))
{
$array = array($array);
}
return $array;
}
}
So you can call it with ArrayTools::CheckArray($array)
Please go with traits if your code mainly involves classes and objects.. As the the concept of traits exclusively focusses on code reuse ability.
Following are the code snippets which actually I use with Plain PHP projects, these code snippets are used from various frameworks good traits and best practices.
1.
The following code is used to check the environment in which your working, based on the environment you can set the some global variables, error reporting as so on.
if(!defined('ENVIRONMENT')){
define('ENVIRONMENT','DEVELOPMENT');
}
if (defined('ENVIRONMENT'))
{
switch (ENVIRONMENT)
{
case 'DEVELOPMENT':
case 'TESTING':
$base_url = 'http://localhost/project_name/';
error_reporting(E_ALL);
break;
case 'PRODUCTION':
$base_url = 'http://hostname/project_name/';
error_reporting(0);
break;
default:
exit('The application environment is not set correctly.');
}
}
2.
/* This function is used to PRINT the ARRAY data in the pre formatted manner */
if (!function_exists('pr')) {
function pr($data) {
echo '<pre>', print_r($data), '</pre>';
}
}
3.
/* This function is used to Sanitize the user data and make data safe to insert into the database */
function sanitize($data) {
global $link;
$data = trim($data);
return htmlentities(strip_tags(mysqli_real_escape_string($link, $data)));
}
4.
/* Used to get the difference of 2 arrays
Returns the array with difference
*/
function multi_diff($arr1,$arr2){
$result = array();
foreach ($arr1 as $k=>$v){
if(!isset($arr2[$k])){
$result[$k] = $v;
} else {
if(is_array($v) && is_array($arr2[$k])){
$diff = multi_diff($v, $arr2[$k]);
if(!empty($diff))
$result[$k] = $diff;
}
}
}
return $result;
}
5.
/* This fnction is used to generate the random keys of specific length
Accepts parameter of certain length if not specified it will generate 20 bit length automatically
*/
function generate_random_key($length = 20) {
//Initializing the varialble
$keystring = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890';
$random_key = '';
for ($i = 0; $i < $length; $i++) {
$random_key.=$keystring[rand(0, strlen($keystring) - 1)];
}
//Return the randomly generated key
return $random_key;
}
6.
/* This function outputs the errors in ul>li format with unstyled
* To get the bullets styling remove class='list-unstyled' in <ul> tag */
function output_errors($errors){
$output = array();
foreach ($errors as $error) {
$output[] = '<li>'.$error.'</li>';
}
return '<ul class="list-unstyled">'.implode('', $output).'</ul>';
}
7.
/* Checks whether the user is loggedin else will redirect to the protectect page */
function protected_page(){
if(is_loggedin() === false){
// header('Location: protected.php');
header('Location: logout.php');
exit();
}
}
8.
/* If user tries to access the page directly accessing through the URL,
* If already loggedin then redirect him to any of the inner page
*/
function login_redirect(){
if(is_loggedin() === true){
header('Location: home.php');
}
}
9.
/* This function is used to check whether the user exists or not */
function email_exists($email){
/* Your Code */
}
/* This function is used to check whether the user isActive or not */
function is_active($email){
/* Your Code */
}
/* This function will get the userid from the email */
function userid_from_email($email) {
/* Your Code */
}
/* This fucntion is used to login the user based on the email-id and password */
function login($email,$password){
/* Your Code */
}
/* Check whether the USER is loggedin or not */
function is_loggedin(){
return (isset($_SESSION['userid'])) ? true : false;
}
Hope this helps you. Cheers!
Here's the code for my original PHP code:
public function outputText() {
$i = 1;
foreach($this->sorted_data as $this->data) {
echo "$i. ".$this->data[0]."<br/>";
$i++;
}
}
And here's the code for the PHPUnit:
public function testVerify() {
$yn = new SortThisData();
$yn->readFile("input.txt");
$output = $yn->outputText();
$this->assertTrue(is_string($output));
//if(!is_string($yn->get()))
// return false;
//$this->assertNotEmpty($yn->get());
}
The class is called SortThisData in the original PHP file.
When I used gettype(), it said it was null. I'm trying to verify that it is a string so it can pass in PHPUnit. Is there a way I can do this?
You're looking for assertInternalType().
Update: I didn't realize you were echoing the output. You will probably need to use output buffering to capture the text.
public function testVerify() {
$yn = new SortThisData();
$yn->readFile("input.txt");
// start output buffering and capture the output
ob_start();
$yn->outputText();
$output = ob_get_clean();
$this->assertInternalType('string', $output);
}
No disagreement with Baylor's answer. To answer the question, as asked, what you had was also good enough:
$this->assertTrue(is_string($output));
Or you could have done:
$this->assertEquals('string',gettype($output));
(The advantage of the latter is, when it fails, it will also tell you the type of $output; assertTrue will only tell you that something failed.)
assertInternalType() does exactly that, but was only introduced in PHPUnit 3.5, and you will still find PHPUnit 3.4 in use on some machines.
I'm trying to write a unit test for a controller using Zend and PHPUnit
In the code I get data from php://input
$req = new Zend_Controller_Request_Http();
$data = $req->getRawBody();
My code works fine when I test the real application, but unless I can supply data as a raw http post, $data will always be blank. The getRawBody() method basically calls file_get_contents('php://input'), but how do I override this in order to supply the test data to my application.
I had the same problem and the way I fixed it was to have the 'php://input' string as a variable that is settable at run time. I know this does not really apply directly to this question as it would require modifying the Zend Framework. But all the same it may be helpful to someone.
For example:
<?php
class Foo {
public function read() {
return file_get_contents('php://input');
}
}
would become
<?php
class Foo {
public $_fileIn = 'php://input';
public function read() {
return file_get_contents($this->_fileIn);
}
}
Then in my unit test I can do:
<?php
$obj = new Foo();
$obj->_fileIn = 'my_input_data.dat';
assertTrue('foo=bar', $obj->read());
You could try mocking the object in your unit tests. Something like this:
$req = $this->getMock('Zend_Controller_Request_Http', array('getRawBody'));
$req->method('getRawBody')
->will($this->returnValue('raw_post_data_to_return'));
Provided the $req->getRawBody() is, as you say, the same as file_get_contents('php://input')...
$test = true; /* Set to TRUE when using Unit Tests */
$req = new Zend_Controller_Request_Http();
if( $test )
$data = file_get_contents( 'testfile.txt' );
else
$data = $req->getRawBody();
Not a perfect solution, but similar to what I have used in the past when designing scripts to handle piped emails with great success.
Zend_Controller_Request_HttpTestCase contains methods for setting and getting various http request/responses.
For example:
$req = new Zend_Controller_Request_HttpTestCase;
$req->setCookie('cookie', 'TRUE');
$test = $this->controller->cookieAction($req);
$this->assertSame($test, TRUE);
I am trying to get some errors returned in JSON format. So, I made a class level var:
public $errors = Array();
So, lower down in the script, different functions might return an error, and add their error to the $errors array. But, I have to use return; in some places to stop the script after an error occurs.
So, when I do that, how can I still run my last error function that will return all the gathered errors? How can I get around the issue of having to stop the script, but still wanting to return the errors for why I needed to stop the script?!
Really bare bones skeleton:
$errors = array();
function add_error($message, $die = false) {
global $errors;
$errors[] = $message;
if ($die) {
die(implode("\n", $errors));
}
}
If you are using PHP5+ your class can have a destructor method:
public function __destruct() {
die(var_dump($this->errors));
}
You can register a shutdown function.
Add the errors to the current $_SESSION
Add the latest errors to any kind of cache, XML or some storage
If the code 'stops':
// code occurs error
die(print_r($errors));
You can use a trick involving do{}.
do {
if(something) {
// add error
}
if(something_else) {
// add error
break;
}
if(something) {
// add error
}
}while(0);
// check/print errors
Notice break, you can use it to break out of the do scope at any time, after which you have the final error returning logic.
Or you could just what's inside do{} inside a function, and use return instead of break, which would be even better. Or yes, even better, a class with a destructor.
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 6 years ago.
Improve this question
I've read everywhere about how great they are, but for some reason I can't seem to figure out how exactly I'm supposed to test something. Could someone perhaps post a piece of example code and how they would test it? If it's not too much trouble :)
There is a 3rd "framework", which is by far easier to learn - even easier than SimpleTest, it's called phpt.
A primer can be found here:
http://qa.php.net/write-test.php
Edit: Just saw your request for sample code.
Let's assume you have the following function in a file called lib.php:
<?php
function foo($bar)
{
return $bar;
}
?>
Really simple and straight forward, the parameter you pass in, is returned. So let's look at a test for this function, we'll call the test file foo.phpt:
--TEST--
foo() function - A basic test to see if it works. :)
--FILE--
<?php
include 'lib.php'; // might need to adjust path if not in the same dir
$bar = 'Hello World';
var_dump(foo($bar));
?>
--EXPECT--
string(11) "Hello World"
In a nutshell, we provide the parameter $bar with value "Hello World" and we var_dump() the response of the function call to foo().
To run this test, use: pear run-test path/to/foo.phpt
This requires a working install of PEAR on your system, which is pretty common in most circumstances. If you need to install it, I recommend to install the latest version available. In case you need help to set it up, feel free to ask (but provide OS, etc.).
There are two frameworks you can use for unit testing. Simpletest and PHPUnit, which I prefer. Read the tutorials on how to write and run tests on the homepage of PHPUnit. It is quite easy and well described.
You can make unit testing more effective by changing your coding style to accommodate it.
I recommend browsing the Google Testing Blog, in particular the post on Writing Testable Code.
I rolled my own because i didnt have time to learn someone elses way of doing things, this took about 20 minutes to write up, 10 to adapt it for posting here.
Unittesting is very usefull to me.
this is kinda long but it explains itself and there is an example at the bottom.
/**
* Provides Assertions
**/
class Assert
{
public static function AreEqual( $a, $b )
{
if ( $a != $b )
{
throw new Exception( 'Subjects are not equal.' );
}
}
}
/**
* Provides a loggable entity with information on a test and how it executed
**/
class TestResult
{
protected $_testableInstance = null;
protected $_isSuccess = false;
public function getSuccess()
{
return $this->_isSuccess;
}
protected $_output = '';
public function getOutput()
{
return $_output;
}
public function setOutput( $value )
{
$_output = $value;
}
protected $_test = null;
public function getTest()
{
return $this->_test;
}
public function getName()
{
return $this->_test->getName();
}
public function getComment()
{
return $this->ParseComment( $this->_test->getDocComment() );
}
private function ParseComment( $comment )
{
$lines = explode( "\n", $comment );
for( $i = 0; $i < count( $lines ); $i ++ )
{
$lines[$i] = trim( $lines[ $i ] );
}
return implode( "\n", $lines );
}
protected $_exception = null;
public function getException()
{
return $this->_exception;
}
static public function CreateFailure( Testable $object, ReflectionMethod $test, Exception $exception )
{
$result = new self();
$result->_isSuccess = false;
$result->testableInstance = $object;
$result->_test = $test;
$result->_exception = $exception;
return $result;
}
static public function CreateSuccess( Testable $object, ReflectionMethod $test )
{
$result = new self();
$result->_isSuccess = true;
$result->testableInstance = $object;
$result->_test = $test;
return $result;
}
}
/**
* Provides a base class to derive tests from
**/
abstract class Testable
{
protected $test_log = array();
/**
* Logs the result of a test. keeps track of results for later inspection, Overridable to log elsewhere.
**/
protected function Log( TestResult $result )
{
$this->test_log[] = $result;
printf( "Test: %s was a %s %s\n"
,$result->getName()
,$result->getSuccess() ? 'success' : 'failure'
,$result->getSuccess() ? '' : sprintf( "\n%s (lines:%d-%d; file:%s)"
,$result->getComment()
,$result->getTest()->getStartLine()
,$result->getTest()->getEndLine()
,$result->getTest()->getFileName()
)
);
}
final public function RunTests()
{
$class = new ReflectionClass( $this );
foreach( $class->GetMethods() as $method )
{
$methodname = $method->getName();
if ( strlen( $methodname ) > 4 && substr( $methodname, 0, 4 ) == 'Test' )
{
ob_start();
try
{
$this->$methodname();
$result = TestResult::CreateSuccess( $this, $method );
}
catch( Exception $ex )
{
$result = TestResult::CreateFailure( $this, $method, $ex );
}
$output = ob_get_clean();
$result->setOutput( $output );
$this->Log( $result );
}
}
}
}
/**
* a simple Test suite with two tests
**/
class MyTest extends Testable
{
/**
* This test is designed to fail
**/
public function TestOne()
{
Assert::AreEqual( 1, 2 );
}
/**
* This test is designed to succeed
**/
public function TestTwo()
{
Assert::AreEqual( 1, 1 );
}
}
// this is how to use it.
$test = new MyTest();
$test->RunTests();
This outputs:
Test: TestOne was a failure
/**
* This test is designed to fail
**/ (lines:149-152; file:/Users/kris/Desktop/Testable.php)
Test: TestTwo was a success
Get PHPUnit. It is very easy to use.
Then start with very simple assertions. You can do alot with AssertEquals before you get into anything else. That's a good way to get your feet wet.
You may also want to try writing your test first (since you gave your question the TDD tag) and then write your code. If you haven't done this before it is an eye-opener.
require_once 'ClassYouWantToTest';
require_once 'PHPUnit...blah,blah,whatever';
class ClassYouWantToTest extends PHPUnit...blah,blah,whatever
{
private $ClassYouWantToTest;
protected function setUp ()
{
parent::setUp();
$this->ClassYouWantToTest = new ClassYouWantToTest(/* parameters */);
}
protected function tearDown ()
{
$this->ClassYouWantToTest = null;
parent::tearDown();
}
public function __construct ()
{
// not really needed
}
/**
* Tests ClassYouWantToTest->methodFoo()
*/
public function testMethodFoo ()
{
$this->assertEquals(
$this->ClassYouWantToTest->methodFoo('putValueOfParamHere), 'expectedOutputHere);
/**
* Tests ClassYouWantToTest->methodBar()
*/
public function testMethodFoo ()
{
$this->assertEquals(
$this->ClassYouWantToTest->methodBar('putValueOfParamHere), 'expectedOutputHere);
}
For simple tests AND documentation, php-doctest is quite nice and it's a really easy way to get started since you don't have to open a separate file. Imagine the function below:
/**
* Sums 2 numbers
* <code>
* //doctest: add
* echo add(5,2);
* //expects:
* 7
* </code>
*/
function add($a,$b){
return $a + $b;
}
If you now run this file through phpdt (command-line runner of php-doctest) 1 test will be run. The doctest is contained inside the < code > block. Doctest originated in python and is fine for giving useful & runnable examples on how the code is supposed to work. You can't use it exclusively because the code itself would litter up with test cases but I've found that it's useful alongside a more formal tdd library - i use phpunit.
This 1st answer here sums it up nicely (it's not unit vs doctest ).
phpunit is pretty much the defacto unit testing framework for php. there is also DocTest (available as a PEAR package) and a few others.
php itself is tested for regressions and the like via phpt tests which can also be run via pear.
Codeception tests are much like common unit tests but are much powerful in things where you need mocking and stubbing.
Here is the sample controller test. Notice how easily stubs are created. How easily you check the method was invoked.
<?php
use Codeception\Util\Stub as Stub;
const VALID_USER_ID = 1;
const INVALID_USER_ID = 0;
class UserControllerCest {
public $class = 'UserController';
public function show(CodeGuy $I) {
// prepare environment
$I->haveFakeClass($controller = Stub::makeEmptyExcept($this->class, 'show'));
$I->haveFakeClass($db = Stub::make('DbConnector', array('find' => function($id) { return $id == VALID_USER_ID ? new User() : null ))); };
$I->setProperty($controller, 'db', $db);
$I->executeTestedMethodOn($controller, VALID_USER_ID)
->seeResultEquals(true)
->seeMethodInvoked($controller, 'render');
$I->expect('it will render 404 page for non existent user')
->executeTestedMethodOn($controller, INVALID_USER_ID)
->seeResultNotEquals(true)
->seeMethodInvoked($controller, 'render404','User not found')
->seeMethodNotInvoked($controller, 'render');
}
}
Also there are other cool things. You can test database state, filesystem, etc.
Besides the excellent suggestions about test frameworks already given, are you building your application with one of the PHP web frameworks that has automated testing built in, such as Symfony or CakePHP? Sometimes having a place to just drop in your test methods reduces the start-up friction some people associate with automated testing and TDD.
Way too much to re-post here, but here is a great article on using phpt. It covers a number of aspects around phpt that are often overlooked, so it could be worth a read to expand your knowledge of php beyond just writing a test. Fortunately the article also discusses writing tests!
The main points of discussion
Discover how marginally documented aspects of PHP work (or pretty much any part for that matter)
Write simple unit tests for your own PHP code
Write tests as part of an extension or to convey a potential bug to the internals or QA groups
I know there is a lot of info here already, but since this still shows up on Google searches i might as well add Chinook Test Suite to the list. It is a simple and small test framework.
You can easily test your classes with it and also create mock objects. You run the tests through a web browser and (not yet) through a console.
In the browser you can specify what test class or even what test method to run. Or you can simply run all tests.
A screenshot from the github page:
What i like about it is the way you assert tests. This is done with so called "fluent assertions". Example:
$this->Assert($datetime)->Should()->BeAfter($someDatetime);
And creating mock objects is a breeze too (with a fluent like syntax):
$mock = new CFMock::Create(new DummyClass());
$mock->ACallTo('SomeMethod')->Returns('some value');
Anyway, more info can be found on the github page with a code example as well:
https://github.com/w00/Chinook-TestSuite