Agile Zone is brought to you in partnership with:

I am a programmer and architect (the kind that writes code) with a focus on testing and open source; I maintain the PHPUnit_Selenium project. I believe programming is one of the hardest and most beautiful jobs in the world. Giorgio is a DZone MVB and is not an employee of DZone and has posted 636 posts at DZone. You can read more from them at their website. View Full User Profile

Practical PHP Testing Patterns: Layer Test

11.22.2010
| 4901 views |
  • submit to reddit

Most of PHP web applications nowadays use a layered architecture: a division into horizontal components, which only know the existence of the nearest layers. For example, many applications are divided into a combination of infrastructure layer (data persistence), a domain model or service layer, and a presentation one which handles the rendering of HTML and the possible user inputs. Even more layers can be inserted in this picture, to deal with the essential complexity of the application: it's usually more practical to deal with N layers of 10K lines of code that with 10NK lines of code which can affect each other.

The Layer Test pattern prescribes to write independent tests for each layer of an application, following through the principal of separating different concerns into different layers. Basically, this pattern applies the principle of unit testing at the whole layer level. Of course, unit tests will also be present for smaller components.

Isolating a layer

Testing the lowest layer is very simple, since it has no fan out towards the other ones. We only have to call the Api it exposes and pretend to be the real overhanging layer.

For the other layers, it is a bit more difficult to execute isolated tests, since you'll have to inject a Test Double layer N-1 in each layer N, at least for the slice of the layer involved in your tests. Regression and changes in a layer would then not affect the tests of the other ones (and the tests of a component officially define if it is working or not.)

Testing in isolation is in general useful to simplify code paths coverage since you can easily reproduce corner cases by modifying the behavior of the Test Double substituting layer N-1. For example, you can make the Test Double generate an exception which would be otherwise difficult to provoke if the whole application would be wired together.

Isolated tests are only needed in a smaller number, since you do not have an explosion of test cases: if you have A code paths in layer 1 and B paths in layer 2, testing them together would result in A*B possible cases. In isolation, you only have A+B cases. If the layers are 4 or 5, you get the picture: less test cases, and each faster since it involves a lower memory usage.

Of course end-to-end testing is important to check that your layers are wired together correctly, but unit testing is much more focused and handy for TDD purposes and for quickly locating bugs after you have discovered their existence. An end-to-end test tells you that there is a bug; a unit test tells you where there is a bug.

Variations

There are many variations of this pattern, depending on which type of layer you're testing in isolation. These are some of the ones cited by Meszaros in the xUnit test patterns book.

Presentation layer tests

At Italian Agile Day 2010, I saw Jacopo Franzoi presenting the testing in isolation of the output of template views. Testing your views by feeding them fake data, to check that the generated structure is correct, is an example of Layer Test, for the presentation layer of course.

Service layer tests

 

As an example, we use mostly generic components for the service layer, and we test them with fake domain model implementations. Boundary conditions are easy to create, even if our domain entities do not allow some exceptions to be thrown yet.

Persistence layer tests

These are often integration tests if you use an Object-Relational Mapper: they check that the configuration of persistence metadata is correct. Persistence tests are mostly round-trip tests, which can contain the handling of with errors in the data if the database is shared with other applications. When persistence has a well-defined contract, like a Repository interface, this test get more interesting, but they do not usually have the problem of Test Doubles creation and insertion.

Subcutaneous test

This kind of tests comprehends mostly functional tests for the service layer, which skips the user interface (presentation layer) but do not stub out the lower layers at all. Those tests are especially  useful for reproducing scenarios without the hassle of presentation concerns: the service layer is already functionally complete with the aid of the lower layers, while the presentation layer may require additional formatting of input and output data, like JSON encoding or other orthogonal concerns.
End-to-end tests require many strange things to be available, like a Selenium Server. If you have an Ajax application, you'll find yourself writing many Layer Test, maybe in the subcutaoneous version.

Example

The code sample shows a test in isolation of a Render class along with a .php script. Therefore, this is a Layer Test for presentation. In this particular case, the Test Double is a simple array which contains the view data (this was the data structure returned from the service layer.)

This is the view script to test:

<h1><?php echo $this->name; ?></h1>
<div class="description"><?php echo htmlentities($this->description); ?></div>

while this is the Render class along with a single test. I used the Heredoc syntax to easily define a matching string, but regular expressions and HTML parsers can be much more effective in getting to the point in presentation layer tests.

<?php
class PresentationLayerTest extends PHPUnit_Framework_TestCase
{
    public function testHtmlOutputConformsToExpectedOne()
    {
        $render = new Render(array(
            'name' => 'Giorgio',
            'description' => 'Computer engineer who <3 PHP'
        ));
        $content = $render->render('layertest.php');
        $expected = <<<EOT
<h1>Giorgio</h1>
<div class="description">Computer engineer who <3 PHP</div>

EOT;
        $this->assertEquals($expected, $content); 
    }
}

class Render
{
    /**
     * @var array
     */
    private $data;

    public function __construct(array $data)
    {
        $this->data = $data;
    }

    public function render($file)
    {
        ob_start();
        include $file;
        $content = ob_get_contents();
        ob_end_clean();
        return $content;
    }

    public function __get($name)
    {
        return $this->data[$name];
    }
}
Published at DZone with permission of Giorgio Sironi, author and DZone MVB.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)