【问题标题】:How to display underlying tested method in PHPUnit tests?如何在 PHPUnit 测试中显示底层测试方法?
【发布时间】:2011-07-15 16:46:29
【问题描述】:

我有包含大量测试的测试套件。这是一个中等大小的:

ok  4 - CommodityBasketTest::testStartsOutEmpty
ok  5 - CommodityBasketTest::testCanAddACommodity
ok  6 - CommodityBasketTest::testWillAddOneCommodityByDefault
ok  7 - CommodityBasketTest::testCannotAddACommodityWithAnNonNumericQuantity
ok  8 - CommodityBasketTest::testAddingTheSameCommodityWillIncreaseItsQuantity
ok  9 - CommodityBasketTest::testMultipleCommodityCanBeAdded
ok 10 - CommodityBasketTest::testTakingFromAnEmptyBasketWontWork
ok 11 - CommodityBasketTest::testTakesFirstCommodityFromTheBasket
ok 12 - CommodityBasketTest::testCanRetrieveASpecificCommodity
ok 13 - CommodityBasketTest::testWillThrowExceptionOnMissingCommodity
ok 14 - CommodityBasketTest::testReturnsZeroWorthForEmptyBaskets
ok 15 - CommodityBasketTest::testReturnsProperWorthOfACommodity
ok 16 - CommodityBasketTest::testWillAccuratelyReturnStatistics

我怎样才能装备 PHPUnit,以便我能以某种方式显示正在测试的底层方法,就像我在粘贴中一样?我的输出很灵活;例如,我想知道CommodityBasketTest::testReturnsZeroWorthForEmptyBaskets 测试CommodityBasket::getValuation()。

这就是我想要的:

-- CommodityBasket::__construct() --
ok  4 - CommodityBasketTest::testStartsOutEmpty

-- CommodityBasket::add() --
ok  5 - CommodityBasketTest::testCanAddACommodity
ok  6 - CommodityBasketTest::testWillAddOneCommodityByDefault
ok  7 - CommodityBasketTest::testCannotAddACommodityWithAnNonNumericQuantity
ok  8 - CommodityBasketTest::testAddingTheSameCommodityWillIncreaseItsQuantity
ok  9 - CommodityBasketTest::testMultipleCommodityCanBeAdded

-- CommodityBasket::take() --
ok 10 - CommodityBasketTest::testTakingFromAnEmptyBasketWontWork
ok 11 - CommodityBasketTest::testTakesFirstCommodityFromTheBasket
ok 12 - CommodityBasketTest::testCanRetrieveASpecificCommodity
ok 13 - CommodityBasketTest::testWillThrowExceptionOnMissingCommodity

-- CommodityBasket::getValuation() --
ok 14 - CommodityBasketTest::testReturnsZeroWorthForEmptyBaskets
ok 15 - CommodityBasketTest::testReturnsProperWorthOfACommodity

-- CommodityBasket::dumpStats() --
ok 16 - CommodityBasketTest::testWillAccuratelyReturnStatistics

感谢您的建议。

【问题讨论】:

  • 这是一个关于代码覆盖率或与代码覆盖率有关的问题吗?
  • 没有。我制作 testdox 文档;我希望能够一目了然地看到哪些方法为它们构建了测试,以及这些测试是什么。
  • 好吧,你总是可以扩展主类。 Zend 也这样做,但我认为他们没有你想要的东西
  • 如果我们谈论的是同一个 testdox,那么它就是一个名为 PHPUnit_Util_TestDox_ResultPrinter_Text 的类。再说一次,除非您已经安装了 PHPUnit 报告和此类,否则我看不出您将如何获得您的列表。结果我只得到了默认的[x] or [ ]。
  • 另外注意到 Theodore 希望按照他们测试的方法对测试进行分组。

标签: php phpunit


【解决方案1】:

我的方法是结合使用@covers 标签和自定义结果打印机。

无论如何你都应该使用@covers 标签来生成更有意义的代码覆盖率,尤其是在更大的测试套件中,确保只有假定的测试很重要测试一个方法真正为它生成覆盖率。

我知道您的问题与报道无关,但我们会在一分钟内解决这个问题。也许只使用该注释对您来说就足够了,因为无论您是否运行所有集成测试等等,每个没有专门测试的方法都会显示 0% 的覆盖率。


一个自定义的结果监听器来收集你想要的信息

实现可以很好地调整,我只是想制作一些可以很好地展示这个概念的东西,并希望给你一些你可以适应的东西。

代码是 alpha 的,因为我只为那个问题编写了它,但它适用于当前的 phpunit,我想我粘贴了你需要的所有内容。

结果:

 --- myClass ---

-- myClass::a --
  ok   - myClassTest::testAone
  fail - myClassTest::testAtwoFails

-- myClass::b --
  ok   - myClassTest::testB

-- myClass::untested --
  !! Method untested !!

我希望这与您想要的输出相匹配。在下面的代码中可以很容易地更改格式。

它会为您要测试的每个班级打印这条信息(一个空的就足够了!)

如果一个测试 @covers 多种方法,它将显示在其中的每一种方法中


Test下的类

<?php

class myClass {



    public function a() {
        return 1;
    }

    public function b() {
        return 2;
    }

    public function untested() {
        return 3;
    }
}

测试用例

<?php

require_once("myClass.php");

class myClassTest extends PHPUnit_Framework_TestCase {

    /**
     * @covers a
     */
    public function testAone() {
        $sut = new myClass();
        $this->assertSame(1, $sut->a());
    }

    /**
     * @covers a
     */
    public function testAtwoFails() {
        $sut = new myClass();
        $this->assertSame("error", $sut->a());

    }

    /**
     * @covers b
     */
    public function testB() {
        $sut = new myClass();
        $this->assertSame(2, $sut->b());
    }

}

你需要注册测试监听的phpunit.xml!

<phpunit>
    <listeners>
        <listener class="ResultPrinterListener" file="./ResultPrinterListener.php"></listener>
    </listeners>
</phpunit>

结果打印机

<?php

class ResultPrinterListener implements PHPUnit_Framework_TestListener {

    protected $suites = array();

    public function addError(PHPUnit_Framework_Test $test, Exception $e, $time) {}
    public function addFailure(PHPUnit_Framework_Test $test, PHPUnit_Framework_AssertionFailedError $e, $time) {}
    public function addIncompleteTest(PHPUnit_Framework_Test $test, Exception $e, $time) {}
    public function addSkippedTest(PHPUnit_Framework_Test $test, Exception $e, $time) {}
    public function startTest(PHPUnit_Framework_Test $test) {}
    public function endTest(PHPUnit_Framework_Test $test, $time) {}
    public function startTestSuite(PHPUnit_Framework_TestSuite $suite) {}

    public function endTestSuite(PHPUnit_Framework_TestSuite $suite) {
        $this->suites[] = $suite;
    }

    public function __destruct() {
        $tests = array();
        foreach($this->suites as $suite) {
            foreach($suite->tests() as $test) {
                if(!$test instanceOf PHPUnit_Framework_TestCase) {
                    continue;
                }

                $testClass = get_class($test);
                $classUnderTest = substr($testClass, 0, -4);  // just cutting the "Test" for now
                /**
                 * Create an array structue
                 *   array[ClassUnderTests][methodUnderTest][arrayOfTestMethodsThatTestThatMethod]
                 * Every method for a class you have at least one test for will show up here for now
                 */
                if(!isset($tests[$classUnderTest])) {
                    if(!class_exists($classUnderTest)) {
                        echo "\nCan't find matching class '$classUnderTest' for test class $testClass!\n";
                    }
                    $class = new ReflectionClass($classUnderTest);
                    foreach($class->getMethods() as $method) {
                        $tests[$classUnderTest][$method->getName()] = array();
                    }
                }
                $annotations = $test->getAnnotations();
                if(!isset($annotations["method"]["covers"])) {
                    continue;
                }

                foreach($annotations["method"]["covers"] as $functionUnderTest) {

                    $statusLine = "";
                    $status = $test->getStatus();
                    if($status == PHPUnit_Runner_BaseTestRunner::STATUS_FAILURE) {
                        $statusLine .= "fail - ";
                    } else if($status == PHPUnit_Runner_BaseTestRunner::STATUS_SKIPPED) {
                        $statusLine .= "skip - ";
                    } else if($status == PHPUnit_Runner_BaseTestRunner::STATUS_INCOMPLETE) {
                        $statusLine .= "inc  - ";
                    } else {
                        $statusLine .= "ok   - ";
                    }
                    $statusLine .= $testClass."::".$test->getName();
                    $tests[$classUnderTest][$functionUnderTest][] = $statusLine;
               }
            }
        }
        foreach($tests as $classUnderTest => $methods) {
            echo "\n\n --- $classUnderTest --- \n\n";
            foreach($methods as $method => $testCaseStrings) {
                echo "-- $classUnderTest::$method -- \n";
                if($testCaseStrings == array()) {
                    echo "  !! Method untested !!\n";
                    continue;
                }
                foreach($testCaseStrings as $testCaseString) {
                    echo "  $testCaseString\n";
                }
                echo "\n";
            }

        }
    }

}

【讨论】:

  • 这真是太棒了,几乎正是我想要的! +150 业力似乎很便宜;o
  • 如果您遇到测试类被命名空间的问题,只需在 __destruct 方法中更改这些行:$testClass = get_class($test); $classUnderTest = get_class($test-&gt;getResult());
猜你喜欢
  • 2017-03-05
  • 1970-01-01
  • 1970-01-01
  • 2021-08-17
  • 1970-01-01
  • 2018-10-31
  • 1970-01-01
  • 1970-01-01
  • 2015-07-21
相关资源
最近更新 更多