Skip to content
Ondřej Machulda edited this page Nov 1, 2017 · 10 revisions

Test dependencies is basically something you want to avoid - it for example violates the FIRST principle and also limits possibility of parallelization.

However, there are certain scenarios in end-to-end tests, when dependency between two tests (or two phases of one tests) may actually occur. For example:

  • You add some item into database, and it takes some time until it is indexed by your fulltext engine, so you can verify the newly added item could be found through the search
  • Your app sends an e-mail (eg. with password reset link) and you want to check this email on IMAP server to verify the reset link works as intended
  • Seeding test fixtures (base data) for your functional tests is time/resources consuming, so you eg. create one user on beginning of your test suite and reuse this user in all other tests requiring an user data.

Two types of dependencies

a) The dependency is just because of order of the test methods (one test must run before the other one) and limited to one test-case class (and you don't need to wait between the two dependent tests). In this case you can use PHPUnit's @depends annotation - see below.

b) You either need to wait some time before tests or the dependency involves more than one test-case, Steward provides a simple way how to define the dependency - you just define @delayAfter and @delayMinutes annotations for the test-case class which is depending on some other class. See below for more information and example usage.

a) Dependency within one test-case class

<?php

namespace My;

class MyTest extends \Lmc\Steward\Test\AbstractTestCase
{
    // method to be run before the other two which depends on it
    public function testPrepareUser()
    {
        $user = new User();

        return $user; // returned data will be passed to the depending test methods
    }

    /**
     * This method will only be run if the method defined in @depends finishes successfully
     *
     * @depends testPrepareUser
     * @param string $user
     */
    public function testDoSomethingWithUser(User $user)
    {
        ...
    }

    /**
     * This is another method depending on the testPrepareUser() method
     *
     * @depends testPrepareUser
     * @param string $user
     */
    public function testDoSomethingElseWithUser(User $user)
    {
        ...
    }
}

b) Dependency involving more than one test-case

To enable this dependency, add these annotations to the test which is depending on other test:

  • @delayAfter - contains fully qualified name of the test-case it depend on, like Foo\Bar\MyTest
  • @delayMinutes (float) - delay in minutes after which this test-case should be run after the test-case defined in @delayAfter; could be also 0 or floating value like 0.1 (= 6 seconds)

First test (to be run before the SecondTest test-case):

<?php

namespace My;

class FirstTest extends \Lmc\Steward\Test\AbstractTestCase
{
    public function testSomething()
    {
    ...
    }
}

SecondTest will be run only after FirstTest (successfully) finishes and after defined delay (1.5 minute) passes:

<?php

namespace My;

/**
 * @delayAfter My\FirstTest
 * @delayMinutes 1.5
 */
class SecondTest extends \Lmc\Steward\Test\AbstractTestCase
{
    public function testSomething()
    {
    ...
    }
}

Ignore dependencies

During development/debugging, you may want to run just the test, which is depending on some other test. To force Steward to ignore dependencies between test-cases (defined using @delayAfter and @delayMinutes annotations), pass to the run command option --ignore-delays (or -i).

# run DelayedExampleTest.php even if it is depending on some other tests
$ steward run dev firefox --pattern DelayedExampleTest.php --ignore-delays

Pass data between dependent test-cases

You may also want to store some data in first test and use them in any depending test-case. For this purpose Steward contains component called Legacy.

Example usage

<?php

namespace My;

use Lmc\Steward\Component\Legacy;

class FirstTest extends \Lmc\Steward\Test\AbstractTestCase
{
    /** @var Legacy */
    private $legacy;

    /** @before */
    public function init()
    {
        // initialize the Legacy component before the test
        $this->legacy = new Legacy($this);
    }

    public function testSeedSomeData()
    {
        //  In real-world we could eg. create some fixture objects on our site etc.
        $data = ['name' => 'Foo Bar', 'phone' => '123 456 789'];
        ...

        $this->legacy->saveWithName($data, 'identifier-of-the-data-seed');
    }
}

Now in the dependent test-case we can load the data like this:

<?php

namespace My;

use Lmc\Steward\Component\Legacy;

/**
 * @delayAfter My\SeedDataTest
 * @delayMinutes 1.5
 */
class SecondTest extends \Lmc\Steward\Test\AbstractTestCase
{
    /** @var array */
    private $data;

    /** @before */
    public function init()
    {
        // Load the data created and stored by the previous test (note we must use the same identifier)
        $this->data = (new Legacy($this))->loadWithName('identifier-of-the-data-seed');
    }

    public function testSomething()
    {
        // we can now access the data like: $this->data['name'];
        ...
    }
}