/usr/portage

Not having globals state doesn't mean you're doomed 11

Clear Dependencies
hbp_pix©

A pretty popular myth about avoiding global state (singletons, multitons, registries, global variable, static variables/methods) is that it results in creating widely used objects more often than needed. The most common example in this case is a database connection. We try to avoid global state to let objects express their dependencies clearly: the object constructor should be as readable as “give me this, give me that and I will work”. Let’s talk about a situation where we instanciate a relatively complex set of domain objects including a service layer. For the example, we assume that we read an existing customer. We use the CustomerServiceLayer to retrieve the Customer, which uses the CustomerRepository to create the Customer object which needs a DatabaseConnection connection and passes a strategy (NameFormattingStrategy) to format the name of the customer to the Customer object and a CustomerDataMapper to allow the Customer object to save itself. Here are the constructor signatures of the involved components:

class Customer ...
    public function __construct(
        NameFormattingStrategy $nameFormattingStrategy
    )
class CustomerRepository ...
    public function __construct(
        DatabaseConnection $connection,
        CustomerDataMapper $dataMapper,
        NameFormattingStrategy $nameFormattingStrategy
    )
class CustomerServiceLayer ...
    public function __construct(CustomerRepository $repository)
class CustomerDataMapper ...
    public function __construct(DatabaseConnection $connection)
class DatabaseConnection ...
   public function __construct(
       string $host,
       int $port,
       string $username,
       string $password,
       string $database
    )

So, all we do in the page controller, may it be a (page) controller or a plain PHP file is instanciating the service layer and its dependencies:

$databaseConnection = new DatabaseConnection(...);
$customerDataMapper = new CustomerDataMapper($databaseConnection);
$nameFormattingStrategy = new NameFormattingStrategy();
$customerRepository = new CustomerRepository(
    $databaseConnection,
    $customerDataMapper,
    $nameFormattingStrategy
);
$serviceLayer = new CustomerServiceLayer($customerRepository);
$customer = $serviceLayer->getCustomerById((int)$_GET['customer_id]);
... pass it to the view, do nifty things ...

If other components, like the OrderRepository needs a database connection, just pass it to it. No need to let the order repository know how to get it. It is just there. In your unit test you can passed a mocked repository, a mocked database connection and a mocked data mapper depending on what particular part of the chain you are going to test. By the way: the heavy construction work could be easily passed to a number of factories just responsible for creating your objects. These factories are easily testable too as the only assertion made would be is the returned object correctly configured.

Filed under , , , & eleven comments & no trackbacks

8 Hints out of Testing-Turmoil 6

  1. Have a continuous integration solution in place. Really. If you don’t, you just burn money by writing tests. I would go so far and say, if you don’t have continuous integration, you should stop writing unit tests and do click testing. Let your CI system generate API docs, high level docs, code coverage report, testdox and every statical analysis info you generate.
  2. The definition of “tests pass” is “tests pass on the continous integration system”. “Works for me” has neither a place in the bugtracker nor everywhere else.
  3. If you can’t test it, the architecture is most likely wrong (exceptions are sessions and caching related code which is generally hard to test). Testability should be your main concern when writing code. What’s the use of fast or wonderful looking code, if you can’t repeatable prove it is working?
  4. Prefer method calls over annotations. A typo in setExpectedException will trigger a transparent error, while a typo in expectedException will lead to Obscure Test, and most likely a Mistery Guest.
  5. Run the whole test harness twice. This will hellp to identify setup/teardown bugs. Create a random test suite to identify the hard to track mistakes.
  6. Run your testsuite really often. We run it with 15 seconds delay every minute and I’m pretty happy with it.
  7. Use good test names that describe the behavior of the unit. The behavior is not the unit you test itself, that’s what I see in the code, it is something like “calling register changes the status of the user to foobar” so the good test name would be “testRegisterChangesTheStatus …”.
  8. Aim for 100% code coverage. 95% is nothing to be proud about, I can guarantee, the missing 5% will be the hardest part.

Filed under , , , & six comments & no trackbacks

Testing exceptions (2) 5

As I previously mentioned we made some effort to better check exceptions. The target is not only ensuring that a various exception is thrown but also that this exception is really the one which is expected. The result was a patch for PHPUnit to test for the exception message (which is already in) and the exception code. As Sebastian mentioned in the PHPUnit 3.2.9 release announcement, the latter is now included too. Therefore a few new options are available.

Example will fail:

<?php
    public function testException()
    {
        $this->setExpectedException('Exception', 'Exception message', 100);
        throw new Exception('Invalid message', 10);
    }

Example will pass:

<?php
    public function testException()
    {
        $this->setExpectedException('Exception', 'Exception message', 100);
        throw new Exception('Exception message', 100);
    }

Specifying exception tests with the annotations syntax:

<?php
    /**
     * @expectedException Exception error message 100
     */
    public function testException()
    {
         throw new Exception('error message', 100);
    }

Another attractive new feature is the option to use namespaces as a test organization separators. Until now, the PEAR scheme was used for PHPUnit test (_ becomes the directory separator, .php is auto-attached). This means a test was called My_ObjectTest and was supposed to be located in My/ObjectTest.php. Thanks to a really simple patch the test location stays the same, while the class name might be changed to My::ObjectTest. Happy namespacing.

Filed under , , & five comments & no trackbacks

Testing exception messages 5

Since PHPUnit 3.2.0 the former extension for exception test case (PHPUnit_Extension_ExceptionTest) has been merged back into the default test case. This allows you to use setExpextedException() in a normal test case. A simple exception test looks like that:

<?php
class MyTest extends PHPUnit_Framework_TestCase
{
    public function testException()
    {
        $this->setExpectedException('Exception');
        throw new Exception();
    }
    /**
     * @expectedException Exception
     */
    public function testException2()
    {
        throw new Exception()
    }
}

Often this is not enough. When you use a Zend Framework alike style for exceptions (one base class to rule them all) you often throw the same exception class with different messages. To test wheither the correct exception has been thrown, I discussed with Sebastian Bergmann about introducing an contains assertion for messages and this patch was applied. So in the current PHPUnit trunk you can test exception messages:

<?php
class MyTest extends PHPUnit_Framework_TestCase
{
    public function testException()
    {
        $this->setExpectedException('Exception', 'Exception message');
        throw new Exception('Exception message');
    }
    /**
     * @expectedException Exception Exception message
     */
    public function testException2()
    {
        throw new Exception('Exception message')
    }
}

As internally PHPUnit uses an assertContains for the exception message you can even test for incomplete strings like an exception message containing a random file name.

I proposed to do the same for exception codes but Sebastian did not agree. But let’s see, maybe that will change when Zend Framework is going to use exception codes a lot.

Filed under , , & five comments & one trackback

Smarty templates and code coverage 0

PHPUnit provides a filter util to mark a piece of code as test fixture. This makes the template disappear from the coverage report and helps a lot to keep the code coverage report readable and clean. In case of Smarty you just use the builtin php-tag to execute native PHP calls:

{php}PHPUnit_Util_Filter::addFileToFilter(__FILE__, 'TESTS');{/php}

Filed under , , , & no comments & no trackbacks

Xinc - Continuous integration toolkit for PHP 0

Since I setting up a preliminary unitesting server is on my todo list for the next time at work I’ve looking at CruiseControl for continuous integration to decide wheither this is worth utilizing or not. I found it a bit unhandy to integrate it in a PHP environment (we are using Zend Framework, PHPUnit and phing to name a few). Today I read about Xinc on Sebastian Bergmanns weblog (thanks by the way: it is a pleasure to work with PHPUnit every day). Maybe this is the tool I was looking for. I will evaluate (and blog about) the current state and if it is already usable.

Filed under , , , & no comments & no trackbacks

Using Phing with PHPUnit3 1

If you are interested in using Phing and PHPUnit >=3.0 together, you can just use this patch. It is heavily based on this work by Kentaro Ishitoya.

Filed under , , , , , , & one comment & no trackbacks