# Balckbox testing PHP functions

While creating the FilepathBot class for my VC implementation I’ve tried to use as much of PHP built-in functions as possible.
In particular the pathinfo function helps me to get some, well, informations about a given path but, aside for its PHP manual page I could not find much information about the return values and possible exceptions.

## Blackbox testing the function

While trying to build a path-parsing function one of the first checks is to make sure that the path itself is a valid path. While the functions file_exits, is_dir and realpath could do the job they all rely on taking absolute paths to do their checks and this is not something the class can use.
The function pathinfo takes a path, a string actually, and wil return some information about it so it should throw an exception if the argument is not a string, right? Wrong.

// test the expectations
public function testPathinfoThrows(){
$this->setExpectedException('ErrorException'); pathinfo(1); // 1 is an int, not a string } And the test will fail because pathinfo throws no errors at all. It actually returns something too and will do the same for almost any argument I will pass it: it returns .. Up-scaling the blackbox testing approach I will use a data-provider in tests to assert that my expectation for pathinfo to return . in any strange case is right  public function pathProvider() { return array( array( 'some', '.' ) , array( '/some', '/' ) , array( 'some/relative/path', 'some/relative' ) , array( '/some/abs/path', '/some/abs' ) , array( '/some/abs/path/', '/some/abs' ) , array( 1, '.' ) , array( '.', '.' ) , array( array() , '.' ) , array( array( 'some', 'foo' ) , '.' ) , array( (object)'some', '.' ) ); } /** * @dataProvider pathProvider */ public function testForPaths($path, $dirname) {$info = pathinfo($path);$this->assertEquals($dirname,$info['dirname']);

The test will fail for non-string parameters like

---------
8) PathTest::testForPaths with data set #7
ErrorException: pathinfo() expects parameter 1 to be string, array given

---------
9) PathTest::testForPaths with data set #8
ErrorException: pathinfo() expects parameter 1 to be string, array given

---------
10) PathTest::testForPaths with data set #9
ErrorException: pathinfo() expects parameter 1 to be string, object given

which means that int are treated like strings. It’s good enough for me.

## What about other return values?

If not explicitly indicated the pathinfo function will return information about:

1. the path dirname
2. the path basename
3. the path extension
4. the path filename

What will it output with the exact same parameters? I will leave the error-raising ones out of the tests

There were 7 failures:

---------
1) PathTest::testForPaths with data set #0
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-'.'
+'some'

---------
2) PathTest::testForPaths with data set #1
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-'.'
+'some'

---------
3) PathTest::testForPaths with data set #2
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-'.'
+'path'

---------
4) PathTest::testForPaths with data set #3
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-'.'
+'path'

---------
5) PathTest::testForPaths with data set #4
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-'.'
+'path'

---------
6) PathTest::testForPaths with data set #5
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-'.'
+'1'

---------
7) PathTest::testForPaths with data set #6
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-'.'
+''

The failing tests are actually telling me the return values, I will use them in tests the next run and will obtain, when all tests wil pass, a map of return values for a given path combination

    public function pathProvider()
{

return array(
array(
'some',                 // original
'.',                    // dirname
'some',                 // basename
null,                   // extension
'some'                  // filename
) ,
array(
'/some',                // original
'/',                    // dirname
'some',                 // basename
null,                   // extension
'some'                  // filename
) ,
array(
'some/relative/path',   // original
'some/relative',        // dirname
'path',                 // basename
null,                   // extension
'path'                  // filename
) ,
array(
'/some/abs/path',       // original
'/some/abs',            // dirname
'path',                 // basename
null,                   // extension
'path'                  // filename
) ,
array(
'/some/abs/path/',      // original
'/some/abs',            // dirname
'path',                 // basename
null,                   // extension
'path'                  // filename
) ,
array(
1,                      // original
'.',                    // dirname
'1',                    // basename
null,                   // extension
'1'                     // filename
) ,
array(
'.',                    // original
'.',                    // dirname
'.',                    // basename
null,                   // extension
''                      // filename
)
);
}
/**
* @dataProvider pathProvider
*/
public function testForPaths($path,$dirname, $basename,$extension, $filename) {$info = pathinfo($path);$this->assertEquals($dirname,$info['dirname']);
$this->assertEquals($basename, $info['basename']); if (!is_null($extension)) {
$this->assertEquals($extension, $info['extension']); }$this->assertEquals($filename,$info['filename']);
}

That’s enough a map to me to move to my production code.

## Could this not be made with some printing to screen?

Yes, absolutely.
But testing practices are made to test functions and methods in batteries and to do so in a consistent manner that skips the whole set-up phase and gives a lot of plus. Just think about testing expectations about Exceptions in a foreach loop. When in doubt I test.