Run PHPUnit Tests in Certain Order
Asked Answered
S

9

82

Is there a way to get the tests inside of a TestCase to run in a certain order? For example, I want to separate the life cycle of an object from creation to use to destruction but I need to make sure that the object is set up first before I run the other tests.

Sherborn answered 13/8, 2008 at 19:2 Comment(2)
You can add @depends as described in an answer below, and using setup() and teardown() is also a good idea, but tests are just run top to bottom...Hemlock
One additional use-case that doesn't seem to have been covered: Maybe all tests are atomic, but some tests are SLOW. I want the fast tests to be run ASAP so they can fail fast, and any slow tests to be run dead last, after I've already seen other problems and can get to them immediately.Achromatize
E
63

Maybe there is a design problem in your tests.

Usually each test must not depend on any other tests, so they can run in any order.

Each test needs to instantiate and destroy everything it needs to run, that would be the perfect approach, you should never share objects and states between tests.

Can you be more specific about why you need the same object for N tests?

Elaelaborate answered 13/8, 2008 at 20:13 Comment(15)
This doesn't seem correct to me. The point of a unit test is to test an entire unit. The point of having a unit is to group things together that have to depend on each other. Writing tests that test individual methods without the context for the class is akin to advocating procedural programming over oo because you're advocating that individual functions should not depend on the same data.Heaven
I disagree with your point of view. The output of an instantiation test is a valid object that can be used by other tests in your test suite. There's no need to instantiate a new object for each test, specially if the constructor is complicated.Engineer
If the constructor is complicated you are doing something wrong, probably your class is doing too much. Please read about "SOLID", more specific about the "Single Responsibility Pattern (SRP)", also you should "fake" the dependencies in your tests using mocks, please read about "mocks, fakes and stubs" too.Elaelaborate
There may also be a practical reason. For instance, if the cleanup you need to do is particualrly time consuming, you might use the tearDownAfterClass function so as to only run it once. If one particular test requires a clean slate, then you either have to make sure that test gets run first, or manually call the tearDownAfterClass function at its start, causing it to be run twice. Yes, this is probably a sign that something is wrong with the test class design, but there are legitimate cases where ordering tests is useful.Statesmanship
At least for database testing reusing objects (at least the connection) is often necessary. PHPUnit refers to that as well: phpunit.de/manual/current/en/database.html (see: Tip: Use your own Abstract Database TestCase)Engird
@Engird if you are testing againt a real database, you are not doing unit tests. You are doing functional tests. To do unit tests, you must mock the DB adapter and, as Fabio says, you need to instantiate your SUT (System Under Test) at each test-run. You can use the protected setUp() method to prepare the mocks if there is stuff you are going to repeat for each test.Predestinate
I don't agree - I am testing a process where a "widget" must be submitted by one person and approved by another, so they must be in order.Puncheon
@Xavi Montero: When actually developing a querybuilder and/or an ORM, you need to execute queries on the corresponding DBMS for a full test. in this specific case, i think this counts as unit testing.Engird
@Engird if the code you are testing is actually the bridge to the DB (for example, the ORM, ODM, etc) (for example you are a developer at the Doctrine project or your company is developing a Doctrine's substitute) you'd probably be mocking the connection object and testing how the connection is consumed to see what queries are created etc. The only test that could have an entry to a real-life DB could be the test for the Connection object itself, and it would access a very controlled real-connection with limited tests.Predestinate
@Engird but if you refer to testing classes that generally access an ORM you "should" mock the ORM itself. Instead, it's often difficult to mock the ORM and for "simplicity" we want to allow the ORM to be "real" (not mocked) and go for a real INSERT or SELECT. [continues in next comment]Predestinate
Okey, I agree with you: For some specific cases, doing a real access to a DB could count as a "unit" test, not "funcional" test with the following considerations: [continues in next comment]Predestinate
a) We know we are breaking the "unit" test, as it tests the consuming_class+ORM instead of the consuming_class only. We will allow this if and only if the ORM is fully tested. Testing one and only class is nearer to "unit testing" and testing two or more classes is nearer to integration testing.Predestinate
b) If we extend the DBUnit and use for instance the datasets utilities to create sets of data and preferrably use in-memory sqlite, is nearer to "unit-testing", but if we connect to a mysql test-database with a copy of the production data it's nearer functional testingPredestinate
c) But for me, finally, the content of the test is actually the one that marks if we are in "unit" or "functional". If we test a single operation; for example that an e-mail adapter logs an event in a database saying "email sent" just after returning from the SMTP call; and say we mock the SMTP but do not mock the DB for simplicity; this could be a "unit test" because we test one single method on a single class EmailAdapter->sendEmail() and check how this altered the state. [continues]Predestinate
Instead, if we test for example that after saving an object we can query it, that is NOT unit but functional. We are testing something like $adapter->save( $object ); and later $actualObject = $adapter->loadById( $id ); - That tests the combination of 2 methods and this is functional testing, not unit testing. If we want to specifically adhere to unit, this should be splitted in 2 tests: One for each unit: test 1) with a setup cleaning the DB, persisting and asserting the DB contains data, test 2) setup sets a fixture, load and assert the loaded object matches the fixture.Predestinate
T
161

PHPUnit supports test dependencies via the @depends annotation.

Here is an example from the documentation where tests will be run in an order that satisfies dependencies, with each dependent test passing an argument to the next:

class StackTest extends PHPUnit_Framework_TestCase
{
    public function testEmpty()
    {
        $stack = array();
        $this->assertEmpty($stack);

        return $stack;
    }

    /**
     * @depends testEmpty
     */
    public function testPush(array $stack)
    {
        array_push($stack, 'foo');
        $this->assertEquals('foo', $stack[count($stack)-1]);
        $this->assertNotEmpty($stack);

        return $stack;
    }

    /**
     * @depends testPush
     */
    public function testPop(array $stack)
    {
        $this->assertEquals('foo', array_pop($stack));
        $this->assertEmpty($stack);
    }
}

However, it's important to note that tests with unresolved dependencies will not be executed (desirable, as this brings attention quickly to the failing test). So, it's important to pay close attention when using dependencies.

Tabina answered 16/12, 2009 at 14:46 Comment(3)
For PHPUnit, this means the test function will be skipped if the previous test weren't executed. That does not create a test order.Revers
Just to expand on @Dereckson, the @depends annotation will cause a test to be skipped if the test that is dependent on either hasn't been run yet or failed when it did run.Gati
@Gati does that mean when we put or write testPop() method before testPush() in the file then testPop() would never be executed and get always skipped?Rajput
E
63

Maybe there is a design problem in your tests.

Usually each test must not depend on any other tests, so they can run in any order.

Each test needs to instantiate and destroy everything it needs to run, that would be the perfect approach, you should never share objects and states between tests.

Can you be more specific about why you need the same object for N tests?

Elaelaborate answered 13/8, 2008 at 20:13 Comment(15)
This doesn't seem correct to me. The point of a unit test is to test an entire unit. The point of having a unit is to group things together that have to depend on each other. Writing tests that test individual methods without the context for the class is akin to advocating procedural programming over oo because you're advocating that individual functions should not depend on the same data.Heaven
I disagree with your point of view. The output of an instantiation test is a valid object that can be used by other tests in your test suite. There's no need to instantiate a new object for each test, specially if the constructor is complicated.Engineer
If the constructor is complicated you are doing something wrong, probably your class is doing too much. Please read about "SOLID", more specific about the "Single Responsibility Pattern (SRP)", also you should "fake" the dependencies in your tests using mocks, please read about "mocks, fakes and stubs" too.Elaelaborate
There may also be a practical reason. For instance, if the cleanup you need to do is particualrly time consuming, you might use the tearDownAfterClass function so as to only run it once. If one particular test requires a clean slate, then you either have to make sure that test gets run first, or manually call the tearDownAfterClass function at its start, causing it to be run twice. Yes, this is probably a sign that something is wrong with the test class design, but there are legitimate cases where ordering tests is useful.Statesmanship
At least for database testing reusing objects (at least the connection) is often necessary. PHPUnit refers to that as well: phpunit.de/manual/current/en/database.html (see: Tip: Use your own Abstract Database TestCase)Engird
@Engird if you are testing againt a real database, you are not doing unit tests. You are doing functional tests. To do unit tests, you must mock the DB adapter and, as Fabio says, you need to instantiate your SUT (System Under Test) at each test-run. You can use the protected setUp() method to prepare the mocks if there is stuff you are going to repeat for each test.Predestinate
I don't agree - I am testing a process where a "widget" must be submitted by one person and approved by another, so they must be in order.Puncheon
@Xavi Montero: When actually developing a querybuilder and/or an ORM, you need to execute queries on the corresponding DBMS for a full test. in this specific case, i think this counts as unit testing.Engird
@Engird if the code you are testing is actually the bridge to the DB (for example, the ORM, ODM, etc) (for example you are a developer at the Doctrine project or your company is developing a Doctrine's substitute) you'd probably be mocking the connection object and testing how the connection is consumed to see what queries are created etc. The only test that could have an entry to a real-life DB could be the test for the Connection object itself, and it would access a very controlled real-connection with limited tests.Predestinate
@Engird but if you refer to testing classes that generally access an ORM you "should" mock the ORM itself. Instead, it's often difficult to mock the ORM and for "simplicity" we want to allow the ORM to be "real" (not mocked) and go for a real INSERT or SELECT. [continues in next comment]Predestinate
Okey, I agree with you: For some specific cases, doing a real access to a DB could count as a "unit" test, not "funcional" test with the following considerations: [continues in next comment]Predestinate
a) We know we are breaking the "unit" test, as it tests the consuming_class+ORM instead of the consuming_class only. We will allow this if and only if the ORM is fully tested. Testing one and only class is nearer to "unit testing" and testing two or more classes is nearer to integration testing.Predestinate
b) If we extend the DBUnit and use for instance the datasets utilities to create sets of data and preferrably use in-memory sqlite, is nearer to "unit-testing", but if we connect to a mysql test-database with a copy of the production data it's nearer functional testingPredestinate
c) But for me, finally, the content of the test is actually the one that marks if we are in "unit" or "functional". If we test a single operation; for example that an e-mail adapter logs an event in a database saying "email sent" just after returning from the SMTP call; and say we mock the SMTP but do not mock the DB for simplicity; this could be a "unit test" because we test one single method on a single class EmailAdapter->sendEmail() and check how this altered the state. [continues]Predestinate
Instead, if we test for example that after saving an object we can query it, that is NOT unit but functional. We are testing something like $adapter->save( $object ); and later $actualObject = $adapter->loadById( $id ); - That tests the combination of 2 methods and this is functional testing, not unit testing. If we want to specifically adhere to unit, this should be splitted in 2 tests: One for each unit: test 1) with a setup cleaning the DB, persisting and asserting the DB contains data, test 2) setup sets a fixture, load and assert the loaded object matches the fixture.Predestinate
W
39

The correct answer for this is a proper configuration file for tests. I had the same problem and fixed it by creating testsuite with necessary test files order:

phpunit.xml:

<phpunit
        colors="true"
        bootstrap="./tests/bootstrap.php"
        convertErrorsToExceptions="true"
        convertNoticesToExceptions="true"
        convertWarningsToExceptions="true"
        strict="true"
        stopOnError="false"
        stopOnFailure="false"
        stopOnIncomplete="false"
        stopOnSkipped="false"
        stopOnRisky="false"
>
    <testsuites>
        <testsuite name="Your tests">
            <file>file1</file> //this will be run before file2
            <file>file2</file> //this depends on file1
        </testsuite>
    </testsuites>
</phpunit>
Wisdom answered 26/7, 2017 at 10:9 Comment(7)
i think this is the only reliable solutionEngird
Perfect! Not every test is a Unit test; when writing HTTP Request or Feature tests, for example, state changes may need to be preserved across test classes, and in such cases, this is the most reliable approach to running tests in a meaningful sequence.Pita
Has someone tested yet, if this is true for parallel execution of PHPUnit tests?Cosma
Is this answer implying that every single test file would need to explicitly be listed, even if there are hundreds of test files? This doesn't look to be a good solution.Ovation
@AttilaSzeremi unfortunately, yes. I did look into this issue since then, so maybe there's a better one now. I believe it is better to have a working (though not perfect) solution than no solution at all :)Wisdom
Instead of <file>file1</file>, you can also use <directory>tests/Service</directory>Tightfisted
This should have been the accepted answer as it clearly answers the question and the issue that tests must not depend on each other is the concern of the developer. Running tests in certain order is more of like a developer taste IMHO, and not necessarily meaning tests should not be dependent.Crenel
S
8

If you want your tests to share various helper objects and settings, you can use setUp(), tearDown() to add to the sharedFixture property.

Schwitzer answered 23/8, 2008 at 22:30 Comment(1)
Can you still assertEquals(), etc in setUp()? Is that bad practice?Filmer
M
7

PHPUnit allows the use of '@depends' annotation which specifies dependent test cases and allows passing arguments between dependent test cases.

Melena answered 29/12, 2009 at 10:22 Comment(0)
G
4

Alternative solution: Use static(!) functions in your tests to create reusable elements. For instance (I use selenium IDE to record tests and phpunit-selenium (github) to run test inside browser)

class LoginTest extends SeleniumClearTestCase
{
    public function testAdminLogin()
    {
        self::adminLogin($this);
    }

    public function testLogout()
    {
        self::adminLogin($this);
        self::logout($this);
    }

    public static function adminLogin($t)
    {
        self::login($t, '[email protected]', 'pAs$w0rd');
        $t->assertEquals('John Smith', $t->getText('css=span.hidden-xs'));
    }

    // @source LoginTest.se
    public static function login($t, $login, $pass)
    {
        $t->open('/');
        $t->click("xpath=(//a[contains(text(),'Log In')])[2]");
        $t->waitForPageToLoad('30000');
        $t->type('name=email', $login);
        $t->type('name=password', $pass);
        $t->click("//button[@type='submit']");
        $t->waitForPageToLoad('30000');
    }

    // @source LogoutTest.se
    public static function logout($t)
    {
        $t->click('css=span.hidden-xs');
        $t->click('link=Logout');
        $t->waitForPageToLoad('30000');
        $t->assertEquals('PANEL', $t->getText("xpath=(//a[contains(text(),'Panel')])[2]"));
    }
}

Ok, and now, i can use this reusable elements in other test :) For instance:

class ChangeBlogTitleTest extends SeleniumClearTestCase
{
    public function testAddBlogTitle()
    {
      self::addBlogTitle($this,'I like my boobies');
      self::cleanAddBlogTitle();
    }

    public static function addBlogTitle($t,$title) {
      LoginTest::adminLogin($t);

      $t->click('link=ChangeTitle');
      ...
      $t->type('name=blog-title', $title);
      LoginTest::logout($t);
      LoginTest::login($t, '[email protected]','hilton');
      $t->screenshot(); // take some photos :)
      $t->assertEquals($title, $t->getText('...'));
    }

    public static function cleanAddBlogTitle() {
        $lastTitle = BlogTitlesHistory::orderBy('id')->first();
        $lastTitle->delete();
    }
  • In this way, you can build hierarchy of you tests.
  • You can steel keep property that each test case is totaly separate from other (if you clean DB after each test).
  • And most important, if for instance, the way of login change in future, you only modify LoginTest class, and you don'n need correct login part in other tests (they should work after update LoginTest) :)

When I run test my script clean up db ad the begining. Above I use my SeleniumClearTestCase class (I make screenshot() and other nice functions there) it is extension of MigrationToSelenium2 (from github, to port recorded tests in firefox using seleniumIDE + ff plugin "Selenium IDE: PHP Formatters" ) which is extension of my class LaravelTestCase (it is copy of Illuminate\Foundation\Testing\TestCase but not extends PHPUnit_Framework_TestCase) which setup laravel to have access to eloquent when we want to clean DB at the end of test) which is extension of PHPUnit_Extensions_Selenium2TestCase. To set up laravel eloquent I have also in SeleniumClearTestCase function createApplication (which is called at setUp, and I take this function from laral test/TestCase)

Gigi answered 17/6, 2016 at 10:31 Comment(1)
Here is more details to run test recorded in Selenium IDE on Laravel 5.2 and phpUnit: #33846328Jennings
W
2

There really is a problem with your tests if they need to run in a certain order. Each test should be totally independent of the others: it helps you with defect localization, and allows you to get repeatable (and therefore debuggable) results.

Checkout this site for a whole load of ideas / information, about how to factor your tests in a manner where you avoid these kinds of issues.

Woolsey answered 14/8, 2008 at 15:39 Comment(1)
PHPUnit supports test dependencies via @depends.Tabina
F
2

In my view, take the following scenario where I need to test creation and destroying of a particular resource.

Initially I had two methods, a. testCreateResource and b. testDestroyResource

a. testCreateResource

<?php
$app->createResource('resource');
$this->assertTrue($app->hasResource('resource'));
?>

b. testDestroyResource

<?php
$app->destroyResource('resource');
$this->assertFalse($app->hasResource('resource'));
?>

I think this is a bad idea, as testDestroyResource depends upon testCreateResource. And a better practice would be to do

a. testCreateResource

<?php
$app->createResource('resource');
$this->assertTrue($app->hasResource('resource'));
$app->deleteResource('resource');
?>

b. testDestroyResource

<?php
$app->createResource('resource');
$app->destroyResource('resource');
$this->assertFalse($app->hasResource('resource'));
?>
Festival answered 28/3, 2010 at 10:42 Comment(1)
-1 In your second approach, destroyResource also depends on createResource, but it's not explicitly set as so. If createResource fails, the UTesting Framework will wrongly point out that destroyResource is not workingMoulin
F
0

I know this is old, but worth a try. Just add numbers to the respective methods and it should run according to your preferred order of execution.

class StackTest extends PHPUnit_Framework_TestCase
{
    public function test_1_Empty()
    {
        // ...
    }

    public function test_2_Push(array $stack)
    {
        // ...
    }

    public function test_3_Pop(array $stack)
    {
        //...
    }
}
Furculum answered 23/6, 2023 at 5:43 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.