DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports
Events Video Library
Over 2 million developers have joined DZone. Join Today! Thanks for visiting DZone today,
Edit Profile Manage Email Subscriptions Moderation Admin Console How to Post to DZone Article Submission Guidelines
View Profile
Sign Out
Refcards
Trend Reports
Events
View Events Video Library
Zones
Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Integrating PostgreSQL Databases with ANF: Join this workshop to learn how to create a PostgreSQL server using Instaclustr’s managed service

[DZone Research] Observability + Performance: We want to hear your experience and insights. Join us for our annual survey (enter to win $$).

Monitoring and Observability for LLMs: Datadog and Google Cloud discuss how to achieve optimal AI model performance.

Automated Testing: The latest on architecture, TDD, and the benefits of AI and low-code tools.

Related

  • The Role of JavaScript in Front-End and Back-End Development
  • A Multilingual Prestashop Online Store Using ChatGPT
  • A Step-By-Step Guide: How To Install a Laravel Script
  • PHP or ASP.NET: Which Powerhouse Platform Prevails for Your Next Project?

Trending

  • A Better Web3 Experience: Account Abstraction From Flow (Part 2)
  • Untangling Deadlocks Caused by Java’s "parallelStream"
  • Embracing Reactive Programming With Spring WebFlux
  • Programming With AI
  1. DZone
  2. Coding
  3. Languages
  4. Practical PHP Testing Patterns: Implicit Teardown

Practical PHP Testing Patterns: Implicit Teardown

Giorgio Sironi user avatar by
Giorgio Sironi
·
Feb. 28, 11 · Interview
Like (0)
Save
Tweet
Share
1.32K Views

Join the DZone community and get the full member experience.

Join For Free

If we define the hooks supported by our test automation framework (PHPUnit for all articles in this series) will call our cleanup code. It's just a matter of placing it in the right place: almost all testing frameworks, throughout many languages, support these hooks.

The tearDown() and tearDownAfterClass() methods (the latter is static) will be executed automatically after the tests. The first, after each test method, before instantiating the next Testcase Object. As the name says, the second after all the test methods in that Testcase Class have been executed.

The contract for this hook (referenced by the Template Method PHPUnit_Framework_TestCase::run()) is that the tear down is always executed, even if the test fails F or gives an error E, or it is skipped or incomplete, and of course it succeeds.

If the framework support this pattern (and PHPUnit does) this is actually the simplest and easier to maintain solution. This pattern is the opposite of In-Line Teardown, but usually requires Automated Teardown, even in a simple form; the hook for starting up the automated process is placed in tearDown().

Implementation

When the teardown phase is equal for all tests, there is no problem in implementing the pattern.

If the teardown procedures are different, the teardown method must be smart enough to understand in which scenario it has been called. There are various tools that PHP provides you:

  • isset() and unset() calls for example have the property of being language constructs (like if() and while()) and not actual functions. This means that isset($array['key']) is the shortest thing you can write which would not fail when $array does not have key as an index (Undefined index notice).
  • foreach() over arrays of resources also useful, as an empty array would also be skipped.

The teardown must clean up not only the final state of different test methods, but also the different exit points of the same method. Maybe an assertion failed and the method exited early, without populating all the fixtures you want to delete. This variation (which is actually the standard way to work) is called Teardown Guard Clause.

In general, tearDown() cleans up what it finds on $this but does not blow up if there are some missing resources. It assumes they were not created in the first place.
So if you want to be sure that your resources are deleted instead of being left to rot in the test suite folders or in some memory location, put a pointer to resources in fields immediately after creation. You never know what can happen between $object = new MyClass() and $this->field = $object.

Always called? Really?

Exceptions are caught inside the run() methods, which calls your test*() one. All of them are managed: catch (Exception $e) does the job very well.

PHP errors like Notices and Warnings are treated by the error handler of PHPUNit, which simply raises exceptions, falling again in the previous case.

The only thing that can prevent a teardown from running is a Fatal Error, like calling a method on null (yes, in PHP that's a Fatal Error).

However this error will cause the process to terminate abruptly: further tests won't fail or become slower and slower because PHP will simply exit. Persistent resources like temporary files or databases however would have to be cleaned up manually.

That's why you have to ensure that your test suite never encounters a Fatal Error, even while tests are red, by placing Guard assertions where appropriate. Before calling a method on a returned value you can easily add a $this->assertInstanceOf() assertion.

It's nice to note that in PHPUnit tearDown() is called also when setUp() raises an early error.

Examples

The sample code (Github reference link) shows you hot to create a tearDown() or a teardownAfterClass() methods. It also shows how to avoid fatal errors that would prevent the teardown phase from being reached by inserting a simple Guard Assertion.

<?php
class ImplicitTeardownTest 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;
private static $classLevelFixtureToTeardown;

public function testExpectMethodForInlineTeardown()
{
$this->fixtureToTeardown = new MyClassWithDestructor(1);
self::$classLevelFixtureToTeardown = new MyClassWithDestructor(2);
$this->assertTrue(false, 'First test failure message.');
}

public function testSomethingElseWhichCouldResultInAFatalError()
{
// suppose your SUT code returns this or a scalar for a
// regression or bug
$object = null;

$this->assertInstanceOf('SplQueue', $object);

$this->assertEquals('dummy', $object->dequeue());
}

/**
* A workaround to being able to support expect() methods
*/
public function tearDown()
{
unset($this->fixtureToTeardown);
}

public static function tearDownAfterClass()
{
// you can't unset() a static property. Don't ask me why
self::$classLevelFixtureToTeardown = null;
}
}

class MyClassWithDestructor
{
private $id;

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

public function __destruct()
{
echo "The instance {$this->id} of MyClassWithDestructor has been destroyed.\n";
}
}
PHP

Opinions expressed by DZone contributors are their own.

Related

  • The Role of JavaScript in Front-End and Back-End Development
  • A Multilingual Prestashop Online Store Using ChatGPT
  • A Step-By-Step Guide: How To Install a Laravel Script
  • PHP or ASP.NET: Which Powerhouse Platform Prevails for Your Next Project?

Comments

Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends: