I'd like to run unit test for a functions library file...
that is, I don't have a class, it's just a file with helper functions in it...
for example, I've created a php project at ~/www/test
and a file ~/www/test/lib/format.php
<?php
function toUpper( $text ) {
return strtoupper( $text );
}
function toLower( $text ) {
return strtolower( $text );
}
function toProper( $text ) {
return toUpper( substr( $text, 0, 1 ) ) . toLower( substr( $text, 1) );
}
?>
tools -> create PHPUnit tests gives me the following error:
PHPUnit 3.4.5 by Sebastian Bergmann.
Could not find class "format" in
"/home/sas/www/test/lib/format.php".
now, if I code (by hand!) the file
~/www/test/tests/lib/FormatTest.php
<?php
require_once 'PHPUnit/Framework.php';
require_once dirname(__FILE__).'/../../lib/format.php';
class FormatTest extends PHPUnit_Framework_TestCase {
protected function setUp() {}
protected function tearDown() {}
public function testToProper() {
$this->assertEquals(
'Sebastian',
toProper( 'sebastian' )
);
}
}
?>
it works fine, I can run it...
but if I select test file from format.php i get
Test file for the selected source file
was not found
any idea?
saludos
sas
ps: another question, is there a way to update generated tests without having to manually delete them???
ps2: using netbeans 2.8 dev
How you've written your Unit test case is 100% correct. The problem lies with common convention and how PHPUnit and Netbeans relies on them.
Best practice these days is to write all your code in an object orientated fashion. So instead of having a PHP file full of utility functions like you have, you wrap these functions into a class and have them as static functions. Here's an example using your code above,
<?php
class Format
{
public static function toUpper($text)
{
return strtoupper($text);
}
public static function toLower($text)
{
return strtolower($text);
}
public static function toProper($text)
{
return self::toUpper(substr($text, 0, 1 )) . self::toLower(substr($text, 1));
}
}
You'd now use your functions like so,
Format::toProper('something');
PHPUnit, and Netbeans, depend on this object orientated philosophy. When you try to automatically generate a PHPUnit test case, PHPUnit looks in your file for a class. It then creates a test case based on this class and it's public API, and calls it ClassNameTest, where ClassName is the name of the class being tested.
Neatbeans also follows this convention and knows that ClassNameTest is a PHPUnit test case for ClassName, so creates a link between the two in the IDE.
So, my advice is to always use classes where you can. If you have utility functions that don't depend on anything and are used globally, make them static ones.
Side note: I'd get rid of your two functions toUpper() and toLower(). There's no need to wrap built in PHP functions when there's no need to. There's also no need to test them as they are thoroughly tested.
Site note 2: There's kind of a built in PHP equivalent to your function toProper() called ucfirst().
Related
I am just starting with PHPunit so not sure if I'm missing something basic but I'd like to have a test case:
scan a particular source directory,
find all files with .PHP extension,
then attempt to instantiate each of the classes represented by these files
test that each class returns the right properties from particular call (all classes in this directory are subtypes of a common base class)
Make sense? The setup code would look like this:
protected function setUp() {
$this->documentFiles = glob('*.php');
$this->documentClasses = array_map(function($file) {
return substr($file,0,count($file)-5); // strip off ".php"
}, $this->documentFiles);
}
And then the instantiation test would be:
public function testInstantiation() {
foreach($this->documentClasses as $class) {
try {
$object = new $class;
} catch (Exception $e) {
fail("Failure to instantiate '{$class}': " + $e.getMessage());
}
}
}
right now the test is passing because the $CWD is not the right directory and consequently it isn't picking up any PHP files.
Is there a way to specify what the working directory should be? Ideally a way which is relative to the root of the project (and therefore abstracts the physical file system).
p.s. my project is PSR-0 compliant for what that is worth
I see at least 2 Unittests:
Testing that all php files with their classes are found
Testing that those classes are called as expected.
Usually you are also testing the filesystem with your tests which is a bad practice. Of course, your called classes need to be tested in separeted tests too in case you haven't done that.
I don't know your class structure so I can only guess you have at least created a class for reading the files fron the given directory, a class that gets the class names (which should be a piece of psr-0 cake) and a class creating the objects from that list. That way you can mock dependencies and the access to the filesystem is reduced to a simple class that can not influence other tests when its mocked.
I suggest using the DirectoryIterator or FilesystemIterator depending on which php version you are using.
Is there a way to specify what the working directory should be?
You can use chdir. You can also just pass a fully qualified path to glob:
glob(__DIR__ . '/*.php');
I don't think your solution is a good one, however. Use a data provider and return an array of instances (or class names as strings). You'll get much better error messages when you test and it won't break when/if you change directory structure.
<?php
class SomeExampleTest extends \PHPUnit_Framework_TestCase
{
public function nameProvider()
{
return array(
array(new Vendor\Package\ClassName()),
array(new Vendor\Package\ClassNameAgain()),
);
}
/**
* #dataProvider nameProvider
*/
public function testSomeMethod($obj)
{
// do some assertions
$this->assertEquals('something', $obj->theMethodYouWantToTest());
}
}
Ok, i've been able to achieve the result I was looking for but I'm wondering if there might be a more savvy "PHPunit" way of doing. Please let me know if you'd do this differently.
I identified that the CWD was the /tests/ directory. This is probably a little fragile as it could easily be run from somewhere further into the test chain but in most cases these days it's fine for me. I can then change my setUp() method to this:
protected function setUp() {
$namespace = "\\path\\to\\classes\\";
$this->documentFiles = glob('../src/path/to/classes/*.php');
$this->documentClasses = array_map(function($file) use ($namespace) {
if (preg_match('/.*\/(.*)\.php/',$file,$matches) === 1)
return $namespace . $matches[1];
else
return null;
}, $this->documentFiles);
}
As I said, it works. I'd love to think there is some more "built-in" way of doing something like this but I'm brand new to PHPunit.
I have a construct like this:
class Foo
{
public function doFoo($value = '')
{
if (function_exists('foo'))
{
return foo($value);
}
// if php version does not support foo()
// ...
}
}
How can i test this code with PHPUnit to achieve a 100% code coverage if the existence of the function foo depends on the installed PHP version (for this example i assume that the function exists, otherwise a code coverage of 100% is impossible). Is there a way to disable/enable PHP functions at runtime?
Any ideas how to solve it?
EDIT:
i have found a solution: the Advanced PHP debugger / PECL extension provide a function *rename_function* for this purposes.
bool rename_function ( string $original_name , string $new_name )
Renames a orig_name to new_name in the global function table. Useful for temporarily
overriding built-in functions.
You can define a simulation of foo() in your test method. Do something like this:
<?php
/**
* Your test method
*/
public function testSomething () {
// if the function not exists, create a simulation
if(!function_exists('foo')) {
function foo() {
return $aValue;
}
}
// your test code ...
}
This works because a function may defined into another method or function. Although they are defined in the method they are globally available.
Using the pecl extension is not required. Further it potentially pollutes the test execution environment and at least adds unneeded requirements.
Furthermore note that PHPUnit as from version 3.6 supports the annotations #codeCoverageIgnore, #codeCoverageIgnoreStart, #codeCoverageIgnoreEnd to can be used to exclude classes, methods or parts of code from coverage analysis.
Well, in PHP 5.3 and higher You can override the behavior of any PHP function.
Look at this
namespace Test
{
function function_exists($function)
{
return false;
}
var_dump(function_exists('var_dump')); // bool(false)
}
This is possible due to the PHP's fallback mechanism. In short, when it encounters a function it first checks if the function exists in the current namespace. If not, it falls back to the global namespace.
I have got a PHP file in which there are some functions (not included in a class). I am using PHPUnit to testing. When I try to generate in a simply way a test file from a file containing functions, the log says:
Could not find class...
Is there any possibility to test functions which are not methods?
Yes, you can with something like this:
includes/functions.php
function my_function() {
return true;
}
tests/MyFunctionTest.php
require_once '../includes/functions.php';
class MyFunctionTest extends PHPUnit_Framework_TestCase
{
public function testReturnValue()
{
$return_value = my_function();
$this->assertTrue($return_value);
}
}
So as long as your function is within scope you can call it from your test method just like any other PHP function in any other PHP framework or project.
If I'm right then PhpUnit works only with classes hence methods. So just convert them into the methods for testing purpose. Shouldn't be hard.
I've already read the documentation. Basically I've tried tests while following the BankAccount example.
But I get errors:
Warning: require_once(PHP/CodeCoverage/Filter.php) [function.require-once]: failed to open stream: No such file or directory in [...]/unitTest/phpunit.php on line 38
Also, the PHP scripts seems to start with #!/usr/bin/env php which indicate they should be run from the console. I'd rather run these from the browser...
Suppose I have a function f1() that returns a string. How should the tests be made? Am I missing something?
A short introduction to the test framework
PHPUnit provides a simple framework for creating a test suite to automate testing of functions and classes. PHPUnit is inspired by JUnit which was created by Kent Beck and Erich Gamma as a tool for eXtreme Programming. One of the rules of XP is to test small software components as often and early as possible, this way you will not have to fix bugs and errors in the API while setting up and testing larger applications which depend on the class. While unit testing is one of the fundimental rules in XP, you don't have to switch to XP to benefit from PHPUnit. PHPUnit stands alone as a good tool for testing classes or a set of functions and will ease your development cycle and help you to avoid endless debug sessions.
Work routine
Normally, you would write a class, do some unsystematic tests using echo() or var_dump(). After this, you use the class in your application and hope everything is ok. To benefit from PHPUnit you should rethink the flow. The best way is to do this:
design your class/API
create a test suite
implement the class/API
run the test suite
fix failures or errors and go to #4 again
It may seem that this will require a lot of time, but this impression is wrong. Creating the test suite using PHPUnit needs only a few minutes and running the test suite only seconds.
Design a class
Let's start with a small example: a string class. First we create a bunch of functions declarations to work on a string:
---- string.php ----
<?php
class String
{
//contains the internal data
var $data;
// constructor
function String($data) {
$this->data = $data;
}
// creates a deep copy of the string object
function copy() {
}
// adds another string object to this class
function add($string) {
}
// returns the formated string
function toString($format) {
}
}
?>
Creating test suite
Now we can create a test suite, which checks every function of your string class. A test suite is normal PHP class inherited from PHPUnit_TestCase containing test functions, identified by a leading 'test' in the function name. In the test function an expected value has to be compared with the result of the function to test. The result of this compare must delegate to a function of the assert*()-family, which decides if a function passes or fails the test.
---- testcase.php ----
<?php
require_once 'string.php';
require_once 'PHPUnit.php';
class StringTest extends PHPUnit_TestCase
{
// contains the object handle of the string class
var $abc;
// constructor of the test suite
function StringTest($name) {
$this->PHPUnit_TestCase($name);
}
// called before the test functions will be executed
// this function is defined in PHPUnit_TestCase and overwritten
// here
function setUp() {
// create a new instance of String with the
// string 'abc'
$this->abc = new String("abc");
}
// called after the test functions are executed
// this function is defined in PHPUnit_TestCase and overwritten
// here
function tearDown() {
// delete your instance
unset($this->abc);
}
// test the toString function
function testToString() {
$result = $this->abc->toString('contains %s');
$expected = 'contains abc';
$this->assertTrue($result == $expected);
}
// test the copy function
function testCopy() {
$abc2 = $this->abc->copy();
$this->assertEquals($abc2, $this->abc);
}
// test the add function
function testAdd() {
$abc2 = new String('123');
$this->abc->add($abc2);
$result = $this->abc->toString("%s");
$expected = "abc123";
$this->assertTrue($result == $expected);
}
}
?>
The first test run
Now, we can run a first test. Make sure that all the paths are correct and then execute this PHP program.
---- stringtest.php ----
<?php
require_once 'testcase.php';
require_once 'PHPUnit.php';
$suite = new PHPUnit_TestSuite("StringTest");
$result = PHPUnit::run($suite);
echo $result -> toString();
?>
If you call this script from the commandline, you will get the following output:
TestCase stringtest->testtostring() failed: expected true, actual false
TestCase stringtest->testcopy() failed: expected , actual Object
TestCase stringtest->testadd() failed: expected true, actual false
Every function fails the test, because your string functions didn't returned what we defined as the expected value.
If you want to call the script through your browser, you have to put the script in a correct html page and call $result->toHTML() instead of $result->toString().
Implementation
Ok, let's start with implementation of the our string class.
---- string.php ----
<?php
class String
{
//contains the internal data
var $data;
// constructor
function String($data) {
$this->data = $data;
}
// creates a deep copy of the string object
function copy() {
$ret = new String($this->data);
return $ret;
}
// adds another string object to this class
function add($string) {
$this->data = $this->data.$string->toString("%ss");
}
// returns the formated string
function toString($format) {
$ret = sprintf($format, $this->data);
return $ret;
}
}
?>
The implementation is complete and we can run the test again:
~>
php -f stringtest.php
TestCase stringtest->testtostring() passed
TestCase stringtest->testcopy() passed
TestCase stringtest->testadd() failed: expected true, actual false
D'oh! the last test failed! We made a typing mistake. Change line 16 in string.php to
<?php
$this->data = $this->data.$string->toString("%s");
?>
and run the test again:
~>
php -f stringtest.php
TestCase stringtest->testtostring() passed
TestCase stringtest->testcopy() passed
TestCase stringtest->testadd() passed
Everything is now OK!
Conclusion
Does it seem like a lot of work for testing three simple functions? Don't forget, this is a small example. Think about bigger, more complex API's like database abstraction or basket classes in a shop application. PHPUnit is an excellent tool to detect errors in the implementation of your class.
Often you will want to reimplement or refactor a large class which is used in several different applications. Without a test suite the likeliness of you breaking something in one of the applications that depends on your class is very high. Thanks to unit tests, you can create a test suite for your class, and then reimplement your class with the security of knowing that as long as the new class passes the tests, applications that depend on the class will work.
Used source: http://pear.php.net
I would really recomment running PHPUnit from Command Line, if possible. At our Company, it is not possible, but we are trying, wich makes our Tests unstable ...
I assume you have to setup some include paths for php with set_include_path(), so phpunit finds the rest of your files. But that may not be enough ...
Our code looks something like this
// you will have to write your own class here that collects the tests
$collector = new Unit_Test_Collector();
$suite = $collector->getSuite();
//$config is an array of phpunit config options
PHPUnit_TextUI_TestRunner::run($suite, $config);
Is it possible to rename a class method in PHP 5.2 during run time? Is it possible to do that using Reflection?
Given:
class Test
{
public function myMethod()
{
echo 'in my method';
}
}
I want to be able to rename myMethod() to oldMethod() so that later I do this:
$test = new Test();
$test->oldMethod(); // in my method
$test->myMethod(); // run time error: method does not exist
From comment below question:
because I would like to install call event handlers on classes, without the classes knowing about it, so I would know that the user is calling a method from a class before that method is actually called.
Solution: use a Decorator.
class EventDecorator
{
protected $_instance;
public function __construct($instance)
{
$this->_instance = $instance;
}
public function __get($prop)
{
printf('Getting %s in %s', $prop, get_class($this->_instance));
return $this->_instance->$prop;
}
public function __set($prop, $val)
{
printf('Setting %s with %s in %s',
$prop, $val, get_class($this->_instance));
return $this->_instance->$prop = $val;
}
public function __call($method, $args)
{
printf('Calling %s with %s in %s',
$method, var_export($args, TRUE), get_class($this->_instance));
return call_user_func_array(array($this->_instance, $method), $args);
}
}
Then you can wrap any class into it:
class Foo
{
public $prop;
public function doSomething() {}
}
$foo = new EventDecorator(new Foo);
$foo->prop = 'bar'; // Setting prop with bar in Foo
$foo->prop; // Getting prop in Foo
$foo->doSomething('something');
// Calling doSomething with array (0 => 'something',) in Foo
This can be fleshed out to provide pre and post hooks. You could also make the Decorator use the Subject/Observer Pattern and fire events to whatever other object registered to the decorator. The above approach is more maintainable and understandable than monkeypatching random methods with runkit.
Additional notes:
You might be interested in the Symfonys EventDispatcher component.
If you are after Aspect Oriented programming, read http://sebastian-bergmann.de/archives/573-Current-State-of-AOP-for-PHP.html - it's from 2006 but there is not much changed in that field since then.
If you are after horizontal reuse, you will like PHP's Trait functionality that is supposed to be in one of the next few versions.
Using Runkits runkit_method_rename you can do this.
For all of you who are asking why you would need this, I have an idea that is very similar. My idea is to attempt to rename an entire PHP Class on the fly. It would be used, in my case, for an IRC Chat bot that I would have load and instantiate plugins on the fly, so that I would no need to reboot the bot and uptime would be very long. This would include renaming of pre-loaded classes of the same name as the class I would be attempting to load, so that there would be no conflict and it would run properly.
For example:
I have $bot running on irc.example.com
I have the plugin test.php installed and working, now when this is loaded into memory I can alter the file test.php without any change to $bot
So I update test.php
Now I want to cause it to load into $bot, but $bot already has a test load in it, and it would conflict if I attempted to include test.php again
so instead, we run a rename function to rename class test to class test[sha1 of a counter]
and then we include 'test.php'
$bot->test = new test();
and there we have it, an updated test plugin installed and loaded into memory on $bot with no reboot.
This is all theory, but it's something to think about before instantly flaming someone for their ideas with the "Why would you even need this" attitude.
I mean, let's be honest here. What are the odds that you're a super genius who knows everything there is to know about programming and would know what everyone would or wouldn't need?