简体   繁体   English

如何在 PHPUnit 测试中显示底层测试方法?

[英]How to display underlying tested method in PHPUnit tests?

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??如何安装 PHPUnit 以便我可以以某种方式显示正在测试的底层方法,就像我在粘贴中一样? I'm flexible on output;我对 output 很灵活; I'd just like to know that CommodityBasketTest::testReturnsZeroWorthForEmptyBaskets tests CommodityBasket::getValuation() , for instance.例如,我想知道CommodityBasketTest::testReturnsZeroWorthForEmptyBaskets测试CommodityBasket::getValuation()

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 .我的方法是结合使用@covers Tag 和自定义结果打印机

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.无论如何,您都应该使用@covers 标签来生成更有意义的代码覆盖率,尤其是在更大的测试套件中,确保只有应该测试方法的测试才能真正为其生成覆盖率,这一点很重要。

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.也许仅仅使用该注释对您来说就足够了,因为无论您是否运行所有集成测试等等,每个没有专门测试的方法都会显示 0% 的覆盖率。


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.该代码是 alpha 代码,因为我只为该问题编写了它,但它适用于当前的 phpunit,我想我粘贴了您需要的所有内容。

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.我希望这符合您想要的 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!)它为您测试的每个 class 打印这条信息(一个空的就足够了!)

If one tests @covers multiple methods it will show up for EVERY ONE of those methods如果一个测试 @covers 多种方法,它将显示在这些方法中的每一种


The class under Test被测class

<?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.xml需要注册测试监听!

<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 "\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";
            }

        }
    }

}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM