如何在 PHPUnit 测试中显示底层测试方法? [英] How to display underlying tested method in PHPUnit tests?

查看:20
本文介绍了如何在 PHPUnit 测试中显示底层测试方法?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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

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

这就是我想要的:

-- CommodityBasket::__construct() --好的 4 - CommodityBasketTest::testStartsOutEmpty-- CommodityBasket::add() --好的 5 - CommodityBasketTest::testCanAddACommodity好的 6 - CommodityBasketTest::testWillAddOneCommodityByDefault好的 7 - CommodityBasketTest::testCannotAddACommodityWithAnNonNumericQuantity好的 8 - CommodityBasketTest::testAddingTheSameCommodityWillIncreaseItsQuantity好的 9 - CommodityBasketTest::testMultipleCommodityCanBeAdded-- CommodityBasket::take() --好的 10 - CommodityBasketTest::testTakingFromAnEmptyBasketWontWork好的 11 - CommodityBasketTest::testTakesFirstCommodityFromTheBasket好的 12 - CommodityBasketTest::testCanRetrieveASpecificCommodity好的 13 - CommodityBasketTest::testWillThrowExceptionOnMissingCommodity-- CommodityBasket::getValuation() --好的 14 - CommodityBasketTest::testReturnsZeroWorthForEmptyBaskets好的 15 - CommodityBasketTest::testReturnsProperWorthOfACommodity-- CommodityBasket::dumpStats() --好的 16 - CommodityBasketTest::testWillAccuratelyReturnStatistics

感谢您的建议.

解决方案

我的方法是结合 @covers 标签和自定义结果打印机.p>

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

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

<小时>

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

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

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

结果:

 --- myClass ----- myClass::a --好的 - myClassTest::testAone失败 - myClassTest::testAtwoFails-- myClass::b --好的 - myClassTest::testB-- myClass::untested --!!方法未经测试!!

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

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

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

<小时>

Test下的类

测试用例

assertSame(1, $sut->a());}/*** @覆盖一个*/公共功能 testAtwoFails() {$sut = new myClass();$this->assertSame("错误", $sut->a());}/*** @covers b*/公共功能测试B(){$sut = new myClass();$this->assertSame(2, $sut->b());}}

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

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

结果打印机

suites as $suite) {foreach($suite->tests() as $test) {if(!$test instanceOf PHPUnit_Framework_TestCase) {继续;}$testClass = get_class($test);$classUnderTest = substr($testClass, 0, -4);//现在只是剪切测试"/*** 创建数组结构* 数组[ClassUnderTests][methodUnderTest][arrayOfTestMethodsThatTestThatMethod]* 你至少有一个测试的类的每个方法现在都会显示在这里*/if(!isset($tests[$classUnderTest])) {如果(!class_exists($classUnderTest)){echo "
Can't find matching class '$classUnderTest' for test class $testClass!
";}$class = new ReflectionClass($classUnderTest);foreach($class->getMethods() as $method) {$tests[$classUnderTest][$method->getName()] = array();}}$annotations = $test->getAnnotations();if(!isset($annotations["method"]["covers"])) {继续;}foreach($annotations["method"]["covers"] as $functionUnderTest) {$statusLine = "";$status = $test->getStatus();if($status == PHPUnit_Runner_BaseTestRunner::STATUS_FAILURE) {$statusLine .= "失败 - ";} else if($status == PHPUnit_Runner_BaseTestRunner::STATUS_SKIPPED) {$statusLine .="跳过-";} else if($status == PHPUnit_Runner_BaseTestRunner::STATUS_INCOMPLETE) {$statusLine .= "inc - ";} 别的 {$statusLine .= "ok - ";}$statusLine .= $testClass."::".$test->getName();$tests[$classUnderTest][$functionUnderTest][] = $statusLine;}}}foreach($tests as $classUnderTest => $methods) {echo "

 --- $classUnderTest --- 

";foreach($methods as $method => $testCaseStrings) {echo "-- $classUnderTest::$method -- 
";如果($testCaseStrings == 数组()){echo " !! 方法未经测试 !!
";继续;}foreach($testCaseStrings 作为 $testCaseString) {echo "$testCaseString
";}回声
";}}}}

I have test suites with lots of tests in them. here is a medium sized one:

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

How can I rig PHPUnit so that I can somehow display the underlying method being tested like I have it in the paste?? I'm flexible on output; I'd just like to know that CommodityBasketTest::testReturnsZeroWorthForEmptyBaskets tests CommodityBasket::getValuation(), for instance.

This is what I'd like:

-- 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

Thank you for your suggestions.

解决方案

My approach would be a combination of the @covers Tag and a custom result printer.

You should use the @covers tag anyways to generate more meaningful code coverage, especially in bigger test suites it is important to make sure that only the tests that are supposed to test a method really generate coverage for it.

I know your question isn't related to coverage but we'll get to that in a minute. And maybe just using that annotation is enough for you as every method that doesn't have a test dedicated to it will show 0% coverage no matter if you run all your integration tests and so forth.


A custom result listener to collect the information you want

The implementation can surly be tuned, i just wanted to produce something that works reasonably well to show of the concept and hopefully give you something you can adapt.

The code is alpha as I've written it only for that question but it works with the current phpunit and I think i pasted everything you need.

The result:

 --- myClass ---

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

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

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

I hope this matches your desired output. The formatting can be changed rather easily in the code below.

It prints this piece of information for every class you have a test for (an empty one is enough!)

If one tests @covers multiple methods it will show up for EVERY ONE of those methods


The class under Test

<?php

class myClass {



    public function a() {
        return 1;
    }

    public function b() {
        return 2;
    }

    public function untested() {
        return 3;
    }
}

The Testcase

<?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());
    }

}

The phpunit.xml you need to register the test listener!

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

The Result Printer

<?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 "
Can't find matching class '$classUnderTest' for test class $testClass!
";
                    }
                    $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 "

 --- $classUnderTest --- 

";
            foreach($methods as $method => $testCaseStrings) {
                echo "-- $classUnderTest::$method -- 
";
                if($testCaseStrings == array()) {
                    echo "  !! Method untested !!
";
                    continue;
                }
                foreach($testCaseStrings as $testCaseString) {
                    echo "  $testCaseString
";
                }
                echo "
";
            }

        }
    }

}

这篇关于如何在 PHPUnit 测试中显示底层测试方法?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆