TestCase Hooks

Hooks provide robust functionality to perform operations around each test. Provides equivalent of PHPUnit's setUp and tearDown methods... with full asynchronous support.

In any reasonably complex test suite there's going to come a need to perform some scaffolding or test setup and ensure that everything has been torn back down. AsyncUnit takes care of this with a series of Attributes annotated on public methods of the TestCase that are called hooks. These methods can perform any scaffolding or tear down that might be needed.

TestCase hooks are only invoked if they are defined on the TestCase being tested. Hooks defined on parent TestCases are NOT implicitly invoked and must be called yourself.

Setup for each test

If you've gotten to this framework then surely you've heard of the awesome PHPUnit? If not, you should absolutely go check it out before you do anything else! After you've done so then you'll be nodding along with the rest of us when I talk about setUp and tearDown, a concept PHPUnit refers to as a "fixture". These two work-horse methods get the brunt of the test scaffolding work. In AsyncUnit instead of methods with specific names we look for a public method annotated with #[BeforeEach] and #[AfterEach] , respectively.

If you wanted to follow PHPUnit's nomenclature we'd implement a TestCase like the following.

<?php

use Cspray\Labrador\AsyncUnit\Attribute\BeforeEach;
use Cspray\Labrador\AsyncUnit\Attribute\Test;
use Cspray\Labrador\AsyncUnit\Attribute\AfterEach;

class MyTestCase extends TestCase {

    private string $bestFramework;

    #[BeforeEach]
    public function setUp() {
        $this->bestFramework = 'AsyncUnit';
    }
    
    #[Test]
    public function whosTheBest() {
        $this->assert()->stringEquals('AsyncUnit', $this->bestFramework);
    }
    
    #[AfterEach]
    public function tearDown() {
        $this->bestFramework = null;
    }
    
}

That's all there is to it! The actual method names that we used here don't actually matter and can be whatever you want; these names were chosen for their familiarity with PHPUnit. Generally we'd advise you to have more semantic names that describe what your setup is actually doing.

TestCase wide setup and tear down

Sometimes you might need to do something that should be done once for all of the tests in a TestCase. Again, using PHPUnit as an example, this would correlate to setUpBeforeClass and tearDownAfterClass. It is important to note that this method must be static.

<?php

use Cspray\Labrador\AsyncUnit\TestCase;
use Cspray\Labrador\AsyncUnit\Attribute\Test;
use Cspray\Labrador\AsyncUnit\Attribute\BeforeAll;
use Cspray\Labrador\AsyncUnit\Attribute\AfterAll;

class MyTestCase extends TestCase {

    private static string $subject;

    #[BeforeAll]
    public static function setUpBeforeClass() {
        self::$subject = 'AsyncUnit';
    }
    
    #[Test]
    public function ensureStaticValueSet() {
        $this->assert()->stringEquals('AsyncUnit', 'AsyncUnit');
    }

    #[AfterAll]
    public static function tearDownAfterClass() {
        self::$subject = null;
    }
}

That is all of the hooks available to a TestCase. These help ensure that tests can be setup and torn down correctly. There are no technical limits on the number of hooks. However, we advise generally having 1 hook or as minimum a number as possible to keep the cognitive overhead low.

When Multiple Hooks are Defined

There is currently a limitation in the framework, due to the nature of the underlying libraries used, where multiple TestCase hooks are invoked in a non-deterministic order. Future versions will include the ability to define a specific order in which test case hooks should be invoked.

Last updated