Test Dependencies in PHPUnit 3.4

Sebastian Bergmann » 13 November 2008 » in New Features » 11 Comments

Back in July, I came across an academic paper (more academic papers on testing that I read recently) titled "JExample: Exploiting Dependencies Between Tests to Improve Defect Localization".

"A well-designed test suite should exhibit high coverage to improve our chances of identifying any defects. When tests fail, we want to quickly localize defects, so we want our attention to be focussed on the relevant failing tests to identify the root cause of the defect. However, when some part of the base-code gets changed, a small defect can cause a domino effect of multiple failing unit tests. This is a problem, because the person changing the code has no other option than to browse all failing unit tests to try and deduce a single root cause. This task can prove to be quite difficult when that person is unfamiliar with the test code that fails."

For the upcoming PHPUnit 3.4 I have implemented support for the idea expressed in the paper mentioned above.

Let us have a look at the following code:

<?php
class DependencyFailureTest extends PHPUnit_Framework_TestCase
{
    public function testOne()
    {
        $this->assertTrue(FALSE);
    }
 
    /**
     * @depends testOne
     */
    public function testTwo()
    {
    }
}
?>

When we run the test above, the execution of the second test will be skipped as the test it depends on (testOne() did not pass:

sb@ubuntu ~ % phpunit --verbose DependencyFailureTest
PHPUnit 3.4.0-dev by Sebastian Bergmann.

DependencyFailureTest
FS

Time: 0 seconds

There was 1 failure:

1) testOne(DependencyFailureTest)
Failed asserting that <boolean:false> is true.
/home/sb/DependencyFailureTest.php:6

There was 1 skipped test:

1) testTwo(DependencyFailureTest)
This test depends on "DependencyFailureTest::testOne" to pass.

FAILURES!
Tests: 2, Assertions: 0, Failures: 1, Skipped: 1.

Some details about this new feature:

  • A test may have more than one @depends annotation.
  • @depends method references the test implemented in method() of the same class.
  • @depends class::method references the test implemented in class::method().
  • PHPUnit does not change the order in which tests are executed, you have to ensure that the dependencies of a test can actually be met before the test is run.

Please play with this new feature and provide feedback about its usefulness and usability.

Defined tags for this entry: , , , ,

Trackback specific URI for this entry

11 Comments to "Test Dependencies in PHPUnit 3.4"

Display comments as (Linear | Threaded)
  1. Henri Bergius
    14/11/2008 at 09:43 Permalink
    ...and in Midgard's test suite we already use this feature :-)

    Reply

  2. René Leonhardt
    14/11/2008 at 14:03 Permalink
    That's a great new feature, thanks!

    I am wondering if it would not be more convenient if @depends and @dataProvider would accept short forms:
    @depends one
    @dataProvider two

    for

    function testOne()
    function providerTwo()

    Reply

  3. Sebastian Bergmann
    14/11/2008 at 14:18 Permalink
    Too much magic, if you ask me.

    Reply

  4. Sam Hennessy
    14/11/2008 at 15:34 Permalink
    I believe this is a very practical and useful move forward in unit testing. I love the ideas and will be pushing the use of this with my colleagues.

    Will you be adding the functionality to have the returned data from a dependency passes as a parameter to the dependent method. I think this will be really great for reducing code in setUp?

    Reply

  5. Mike Naberezny
    02/12/2008 at 21:06 Permalink
    Test failures often cascade within the same class. It would be useful to have a class-level annotation that would instruct PHPUnit to skip the remaining tests in a class once a failure in that same class occurs.

    Reply

  6. Topbit
    16/01/2009 at 19:16 Permalink
    I'm with Mike on this, though maybe also with an addition to the PHPUnit_Framework_Assert::markTestSkipped() function to be able to skip the rest of the tests in the class (as a second parameter, or a new function).

    In the documentation for markTestSkipped(), (seeing if the mysqli extension was loaded), every test would show as skipped - which is largely redundant after the first instance.

    Reply

  7. Francesco
    15/05/2009 at 18:26 Permalink
    Hi,

    I find this extension very useful, though i'm still not able to use it: I'm trying to get the version 3.4 of phpunit from pear following the instructions in:

    http://www.phpunit.de/manual/3.4/en/installation.html

    but i always get the version 3.3.16. How can I get it?

    Thanks in advance

    Reply

  8. Mike Gerwitz
    27/08/2009 at 16:33 Permalink
    Excellent feature. However, it doesn't appear to work with @dataProvider. I cannot pass arguments with both. They could be a very powerful duo if they were able to work together.

    Reply

  9. David Stockton
    18/09/2009 at 06:24 Permalink
    Mike,

    I just put together a quick test that utilizes @depends and @dataProvider and it's working perfectly. Perhaps there's something else going on in your code that's causing a failure in your specific situation?

    David

    Reply

  10. gx
    24/04/2010 at 12:14 Permalink
    About my previous message (on the "not printing dot" issue), I simply fixed it by doing the following (version 3.4.12):

    in TextUI/ResultPrinter.php:
    moved the ``$this->lastTestFailed = FALSE;'' instruction
    from endTest (last line of function [635 for my version])
    to startTest (inserted before first line [605]).

    What about that change? Is it a good fix (or could it possibly induce some bad side effects)?
    Thank you

    Reply

  11. gx
    26/04/2010 at 15:43 Permalink
    [@MODERATION: all my PHP code was stripped out :( I re-post without the php tags, is it possible to delete/edit the "wasted" version? :s Thank you]

    My "previous message" was not validated, maybe too tangled... Simplified, it was:
    Run this test class:

    class DependencySkipTest extends PHPUnit_Framework_TestCase
    {
    public function testA() {$this->assertTrue(FALSE);}
    /** @depends testA */
    public function testB() {}

    public function testC() {$this->assertTrue(TRUE);}
    }

    Expected output:
    FS.
    Actual output:
    FS
    The "." for testC is not printed in PHPUnit_TextUI_ResultPrinter::endTest, because $lastTestFailed has been set to TRUE (in addSkippedTest) when testB was skipped, then has NOT been reset to FALSE before testC (endTest was not called for testB (neither was startTest), which is logical).

    Now you can understand what I wanted to "fix" ;)
    Hope this helps

    Reply

2 Trackbacks to "Test Dependencies in PHPUnit 3.4"

  1. Sebastian Bergmann 25/02/2009 at 11:28
    As mentioned earlier, PHPUnit 3.4 adds support for Test Dependencies as introduced by Kuhn et. al. in JExample: Exploiting Dependencies Between Tests to Improve Defect Localization.Today I implemented fixture reuse based on test dependency information:&lt
  2. Sebastian Bergmann 18/08/2009 at 12:39
    The first release candidate of PHPUnit 3.4 is now available. Among the many new features introduced in this new version, the most notable are the support for test dependencies and fixture reuse as well as the possibility to run tests in separate PHP pr

Add Comment


To prevent automated Bots from commentspamming, please enter the string you see in the image below in the appropriate input box. Your comment will only be submitted if the strings match. Please ensure that your browser supports and accepts cookies, or your comment cannot be verified correctly.
CAPTCHA

Submitted comments will be subject to moderation before being displayed.