Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Practical PHP Testing Patterns: In-Line Teardown

DZone's Guide to

Practical PHP Testing Patterns: In-Line Teardown

· Web Dev Zone ·
Free Resource

Jumpstart your Angular applications with Indigo.Design, a unified platform for visual design, UX prototyping, code generation, and app development.

The In-Line Teardown pattern consists in putting the logic for teardown of resources used in the test metod inside the method as well.

This pattern is the complementary way to work with respect to Automated Teardown and Implicit Teardown (which will be the subject of the next article.)
In the interested test method, we simply add a fourth phase, after arrange, act, and assert, that takes care of cleaning up the resources which won't be automatically deleted.

While objects of course will be garbage-collected, we're talking mainly of external ones: connections, rows which have been created in the database, fixture files. The job of teardown pattern is ensuring that they're not around anymore to occupy memory and slow down the test suite, or worst, spilling over into other tests which should be executed in a fresh environment.

This pattern is the absolute minimum you need to implement when you have some teardown to perform. Other patterns, like Automated Teardown, require a bit greater investment of time (extracting one or two methods; wow!) but they scale well when there are more resources involved (multiple files, many tables to truncate, and so on).

When to adopt In-Line Teardown

For example, when each test has different objects to clean up, or they differ not only in instance but also in type (logic to delete them.)

When only some tests (not a majority) need a clean up phase, this is a sane case for In-Line Teardown. Automated Teardown or Implicit Teardown are almost always better, also from the point of view of implementation simplicity.

Implementation

As it's said in the xUnit Testing Patterns book, to implement this pattern you have to "mentally keep track" of objects created in the test method, which we want to clean up manually.

Then you can add the related code at the end of the test method, but it's not that simple. Note that if a test method fails, an exception will be thrown prior to the teardown, preventing it from being reached.

Note also that teardown should not raise additional errors. Imagine a test which fails only because of its teardown phase...

Variations

  • Naive In-Line Teardown is the most simple form of this pattern. One or more lines of logic are inserte in the test method, without any precautions. It's however viable when teardown is performed for performance and not for consistency: it's not a big deal if it's skipped in case of an isolated failing test.
  • Teardown Guard Clause: a clause is put around the logic for teardown, like a try catch block; if the teardown generates errors, they are ignored.
  • Delegated Teardown: move the logic in a extracted method which is called at the end of the test; the test is shorter and easier to read. This isn't strictly In-Line, but you can compare it with Automated Teardown. In the latter, the teardown methods already knows what to clean up because a more complex mechanism is in place and keeps track of the resources; Here instead, you pass the resource to clean up to the utility method, which only know how to teardown a generic input of that type.

Example

The main problem to solve for In-Line Teardown is that when an assertion fails, it throws an exception and the end of the test, where the teardown phase is placed, is not executed as the exception bubbles up.

There are different solutions to address this issue. A start can be using expect() methods instead of assert() ones, but PHPUnit does not provide you with them by default.
In other languages, the finally block is used. What is the finally block? You're right to ask, since in PHP it does not exist. Is a block akin to catch(), but it is executed regardlessly of the code before throwing an exception. Fortunately, it can be easily emulated, and I'll show you how in this code sample. Its behavior is:

  • if there is an exception raised in try, the exception bubbles up but finally is executed.
  • If there is no exception raised in try, finally is executed after the try block has finished.

As this bug report requesting finally in PHP says, you can also deallocate resources in a finally-like way by using an object with a destructor (but you have to ensure that the object is unset() or loses all its references). It's already complicated to say it, so my examples would use movement of assertions to the last lines and finally-like solutions.

Since some forms of this pattern are complex and tricky. consider moving to Implicit Teardown, described in the next article, where the teardown is always executed regardless of error conditions, thanks to the testing frameworks hooks.

Here is the code, also on Github.

<?php
/**
* All the tests will be failing. This is to demonstrate the difficult part
* of implementing In-Line Teardown: ensure it is executed while test are
* failing.
*/
class InLineTeardownTest extends PHPUnit_Framework_TestCase
{
/**
* I keep the object on a field so that if In-Line Teardown is not
* executed, we'll see destruction messages at the end of the suite
* instead of after each test.
*/
private $fixtureToTeardown;

public function testExpectMethodForInlineTeardown()
{
$this->fixtureToTeardown = new MyClassWithDestructor();
// this doesn't *immediately* throw an exception
$this->expectTrue(false, 'First test failure message.');
unset($this->fixtureToTeardown);
}

private $expectationBooleans = array();
private $expectationErrorMessages = array();

private function expectTrue($boolean, $message = '')
{
$this->expectationBooleans[] = $boolean;
$this->expectationErrorMessages[] = $message;
}

/**
* A workaround to being able to support expect() methods
*/
public function tearDown()
{
foreach ($this->expectationBooleans as $i => $boolean) {
$this->assertTrue($boolean, $this->expectationErrorMessages[$i]);
}
}

public function testMovementOfAssertionsAfterInLineTeardown()
{
$this->fixtureToTeardown = new MyClassWithDestructor();
$result = 1 == 0; // or whatever computation over the results
// that ultimately produces a boolean
unset($this->fixtureToTeardown);
$this->assertTrue($result, 'Second test failure message.');
}

public function testFinallyLikeSolutions()
{
$this->fixtureToTeardown = new MyClassWithDestructor();
try {
$this->assertTrue(false, 'Third test failure message.');
} catch (Exception $assertionException) {
unset($this->fixtureToTeardown);
throw $assertionException;
}
}
}

class MyClassWithDestructor
{
public function __destruct()
{
echo "The instance of MyClassWithDestructor has been destroyed.\n";
}
}

Take a look at an Indigo.Design sample application to learn more about how apps are created with design to code software.

Topics:

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}