Tuesday, May 19, 2009

Static Methods versus Object Instantiation

One of the arguments I have been having in my team lately is whether our models should be a set of static methods or an object that is instantiated with public methods. My background is such that I tend to lean towards object instantiation in 95% of the objects I develop. In addition, many of the objects with all static methods are simply a method for namespacing global functions. Using objects this way is 100% against my understanding of object oriented development.

Wikipedia (Objects) definitionIn computer science, an object commonly means a data structure consisting of data fields and procedures (methods) that can manipulate those fields.

Therefore a class of methods that behaves like a toolbox is simply not object oriented development. With that said, I read a good article yesterday while searching for more information about the two sides of the arguments that basically stated that unless the object has a persistent state beyond a single method invocation it might be worthwhile to consider making it public static. Reading this simply, if the object has no properties then it is a candidate to be a set of static methods.

In regards to Models, the problem I have with this is that I strongly believe that the database adapter should be decoupled from the Model itself. If all methods of a Model are static then this means the database adapter would be a required parameter for each method. This added complexity is unnecessary. In my opinion the database adapter should be a required parameter for instantiating a Model. After all, a Model is designed to interact with a data store so providing the adapter to do so makes a lot of sense. This decouples it and reduces the complexity in the parameters for each method the model implements.

This is synonymous of my approach to development in general in that I prefer to divide responsibilities into smaller, simpler methods. This facilitates easier and more complete unit testing in that smaller, simpler methods do not have many paths to traverse therefore testing every path is clear and straightforward. In addition, this approach reduces complexity of the code which is a common practice for improving flexibility and maintainability of code. If a method is not complex it is easier to understand what it is accomplishing as well as to refactor or add functionality.

This discussion has made me rethink other objects of our stack though. I realize that I have under utilized classes that have static methods. I still do not agree that methods should be simply made static and put in a class together if they do not actually represent an object. But I do see greater utility in static methods in general and will implement them more frequently.

Monday, April 13, 2009

Eclipse, PDT, and occurrences or annotations

One of the features I really liked in the old PHPEclipse for Eclipse was the marking of occurrences or annotations(occurrences going forward). This functionality allows a developer to single click on a variable name and all occurrences of that variable (with the same name) are highlighted and marked in the right side occurrences bar. This can be very useful when working with legacy code or when you have a controller that is becoming rather complex. For those averse to using the mouse, moving the cursor to any position in the middle or beginning/end of a variable will act as if the variable were single clicked.

Recently I reinstalled Eclipse to remove the plugin developer environment(PDE) and suddenly the occurrences were not being marked in my PHP code. I was distraught because anyone who works in eclipse knows that all settings are stored in your workspace and not in the program directory so this setting should not have changed. Had some of my settings been wiped? Luckily after visiting PDT's home I found a ticket reference and some documentation around the fix for this behavior. By going into my preferences a la Windows->Preferences->PHP->Editor->Mark Occurrences I was able to re-enable this behavior and avert certain disaster! :P I don't ever recall explicitly setting this feature prior to the reinstall so maybe some oddity caused it to work prior to the re-install. Either way I must say that I still love Eclipse very much! <3

Thursday, January 29, 2009

Using PHPUnit's dataProvider for comprehensive failure testing

Writing tests to make sure your application handles failure conditions appropriately is one of the more difficult things to do. It's much easier to write a unit test case to make sure a function or method works when correct parameters are passed. PHPUnit provides some features that will enable you to easily write robust tests to test those failure conditions. Today I'm going to talk about using PHPUnit's dataProvider functionality.

Suppose you had the following static method:

class Example
{

    /**
     * An example method
     *
     * @param   array  $data
     * @return  void
     */
    public static function one($data)
    {

        if (!is_array($data)) {
            throw new Exception('Array expected');
        }

        if (!array_key_exists('one', $data)) {
            throw new Exception('Key (one) is required');
        }

        if (!array_key_exists('two', $data)) {
            throw new Exception('Key (two) is required');
        }

        $data['one'] = true;
        $data['two'] = false;
        return $data;
    }
}

Let's use PHPUnit's dataProvider to test all the failure conditions of the method Example::one(). Data providers must return an array of arrays representing the parameter set of the test method. In this case we are going to have a test method that accepts 3 parameters; a data set, an exception type, and an exception message.

class ExampleTest extends PHPUnit_Framework_TestCase
{
    /**
     * Test failure
     *
     * @test
     * @dataProvider failureDataProvider
     * @return  void
     */
    public function failure($data, $exception, $message)
    {
        $this->setExpectedException($exception, $message);
        Example::one($data);
    }
}

As you will notice, the PHPDoc of the test method defines the data provider method name using the '@dataProvider' tag. Let's define the data provider.

    /**
     * Failure Data Provider
     * @return  array
     */
    public function failureDataProvider()
    {
        return array(
            array('', 'Exception', 'Array expected'),
            array(false, 'Exception', 'Array expected'),
            array('foo', 'Exception', 'Array expected'),
            array(new Example(), 'Exception', 'Array expected'),
            array(array(), 'Exception', 'Key (one) is required'),
            array(array('somekey' => 'somevalue'), 'Exception', 'Key (one) is required'),
            array(array('one' => 'somevalue'), 'Exception', 'Key (two) is required'),
        );
    }

In this data provider we test all the possible failure conditions for the method Example::one(). Using a data provider keeps your tests clear and concise and enables the addition of additional test parameters with ease. All you need to do is simply add another array to test a new value. Let's see the result of our test.

# phpunit --verbose ExampleTest.php
PHPUnit 3.3.9 by Sebastian Bergmann.

ExampleTest
 ExampleTest::failure
 .......

Time: 0 seconds

OK (7 tests, 14 assertions)

The data provider functionality makes it easy to test large combinations of parameters for any method. Read more about the data provider functionality in the PHPUnit documentation.