为什么PhpUnit数据提供者会降低覆盖率?

时间:2017-04-04 15:32:31

标签: php phpunit code-coverage php-code-coverage

我一直在使用PhpUnit,效果很好,仅使用断言测试我的代码。最近我决定尝试PhpUnit进行覆盖率报告分析,但我注意到使用dataProvider方法的测试往往会降低我的代码覆盖率。我想知道我可能做错了什么或者这是否是dataProvider测试技术的结果?我正在使用PhpUnit 6和Php 7。

我已经包含了一个源类Foo,下面有三个测试类来测试它。 FooTest使用常规测试方法,没有dataProviders。 BarTest使用带有@codeCoverageIgnore注释的dataProvider方法,而BazTest使用没有注释的dataProvider方法。

您可以通过BazTest查看代码覆盖率得分如何降低。

Terminal screenshot of phpunit invocation.

Code Coverage report screenshot with all test classes.

Code Coverage report screenshot with BazTest detail.

Foo.php

namespace phpunittestproject\src;


/**
 * Foo
 * 
 * This is a simple class to be used as a source file
 * in unit test experiments. It gets and sets a name
 * string and date object.
 *
 */
class Foo
{



    /**
     * Name
     * 
     * String characters other than numeric.
     * 
     * @var string
     */
    private $name = null;

    /**
     * Date
     * 
     * Date no older than 2000.
     * 
     * @var \DateTime
     */
    private $date = null;


    /**
     * Constructor
     * 
     * Sets instance vars.
     * 
     */
    public function __construct()
    {

        $this->name = '';
        $this->date = new \DateTime('now');

    }


    /**
     * Get Date
     * 
     * @return DateTime
     */
    public function getDate()
    {
        return $this->date;
    }

    /**
     * Get Name
     * 
     * @return string
     */
    public function getName()
    {
        return $this->name;
    }

    /**
     * Set Date
     * 
     * This method accepts a DateTime object that corresponds
     * to a date no earlier than 2000.
     * 
     * @param \DateTime $value Date after 2000.
     * @return boolean Result of operation.
     */
    public function setDate(\DateTime $value)
    {
        if($value < new \DateTime('2000-01-01 00:00:00')){
            return false;
        } else {
            $this->date = $value;
            return true;
        }
    }

    /**
     * Set Name
     * 
     * This method accepts a string that does not contain numeric
     * characters.
     * 
     * @param string $value String without numeric characters.
     * @return boolean Result of operation.
     */
    public function setName(string $value)
    {
        if(preg_match('/\\d/', $value)){
            return false;
        } else {
            $this->name = $value;
            return true;
        }
    }

}

FooTest.php:

declare(strict_types = 1);

namespace phpunittestproject\test;

use \phpunittestproject\src\Foo;

/**
 * 
 * Foo Test
 * 
 * This test class does not use dataProvider methods. All
 * test assertions are being done in test metods.
 *
 */
class FooTest extends \PHPUnit\Framework\TestCase
{


    /**
     * 
     * 
     */
    public function testGetDate()
    {
        $foo = new Foo();

        // Good date:
        $date = new \DateTime('2011-01-01 11:11:11');
        $foo->setDate($date);
        $this->assertEquals($date, $foo->getDate());

        // Bad date:
        $date = new \DateTime('1990-01-01 11:11:11');
        $foo->setDate($date);
        $this->assertNotEquals($date, $foo->getDate());

    }

    /**
     *
     * 
     */
    public function testGetName()
    {
        $foo = new Foo();

        // Good name:
        $foo->setName('A Good Name');
        $this->assertEquals('A Good Name', $foo->getName());

        // Bad name:
        $foo->setName('Bad Name 666');
        $this->assertNotEquals('Bad Name 666', $foo->getName());

    }

    /**
     * Test setDate()
     * 
     * This test method tests the date property when it is
     * set with good and bad data using method setDate().
     * 
     */
    public function testSetDate()
    {
        $foo = new Foo();

        // Good date:
        $date = new \DateTime('2011-01-01 11:11:11');
        $foo->setDate($date);
        $this->assertAttributeEquals($date, 'date', $foo);

        // Bad date:
        $date = new \DateTime('1990-01-01 11:11:11');
        $foo->setDate($date);
        $this->assertAttributeNotEquals($date, 'date', $foo);

    }

    /**
     *
     * Test setName()
     * 
     * This test method tests the name property when it is
     * set with good and bad data using method setName().
     * 
     * 
     */
    public function testSetName()
    {
        $foo = new Foo();

        // Good name:
        $foo->setName('Good Name');
        $this->assertAttributeEquals('Good Name', 'name', $foo);

        // Bad name:
        $foo->setName('Bad Name 666');
        $this->assertAttributeNotEquals('', 'name', $foo);

    }

}

BarTest.php:

declare(strict_types = 1);

namespace phpunittestproject\test;

use \phpunittestproject\src\Foo;

/**
 * 
 * Bar Test
 * 
 * This test class utilizes dataProvider methods to feed
 * test methods. The dataProvider methods are annotated
 * with codeCoverageIgnore.
 * 
 */
class BarTest extends \PHPUnit\Framework\TestCase
{


    /**
     * 
     * @codeCoverageIgnore
     */
    public function providerTestSetDateWithInvalidData()
    {
        return array(
                array(new \DateTime('1990-01-01 11:11:11')),
        );
    }

    /**
     *
     * @codeCoverageIgnore
     */
    public function providerTestSetDateWithValidData()
    {
        return array(
                array(new \DateTime('2011-01-01 11:11:11')),
        );
    }

    /**
     *
     * @codeCoverageIgnore
     */
    public function providerTestSetNameWithInvalidData()
    {
        return array(
                array('Bad Name 666'),              
        );
    }

    /**
     *
     * @codeCoverageIgnore
     */
    public function providerTestSetNameWithValidData()
    {
        return array(
                array('Good Name'),
        );
    }

    /**
     * 
     * 
     */
    public function testGetDate()
    {
        $foo = new Foo();
        $date = new \DateTime('2001-01-01 11:11:11');
        $foo->setDate($date);
        $this->assertEquals($date, $foo->getDate());
    }

    /**
     *
     * 
     */
    public function testGetName()
    {
        $foo = new Foo();
        $foo->setName('A Good Name');
        $this->assertEquals('A Good Name', $foo->getName());
    }

    /**
     *
     * @dataProvider providerTestSetDateWithInvalidData
     * 
     * 
     */
    public function testSetDateWithInvalidData($value)
    {
        $foo = new Foo();
        $foo->setDate($value);
        $this->assertAttributeNotEquals($value, 'date', $foo);
    }

    /**
     *
     * @dataProvider providerTestSetDateWithValidData
     *
     *
     */
    public function testSetDateWithValidData($value)
    {
        $foo = new Foo();
        $foo->setDate($value);
        $this->assertAttributeEquals($value, 'date', $foo);
    }

    /**
     *
     * @dataProvider providerTestSetNameWithInvalidData
     * 
     * 
     */
    public function testSetNameWithInvalidData($value)
    {
        $foo = new Foo();
        $foo->setName($value);
        $this->assertAttributeNotEquals($value, 'name', $foo);
    }

    /**
     *
     * @dataProvider providerTestSetNameWithValidData
     *
     *
     */
    public function testSetNameWithValidData($value)
    {
        $foo = new Foo();
        $foo->setName($value);
        $this->assertAttributeEquals($value, 'name', $foo);
    }

}

BazTest.php:

declare(strict_types = 1);

namespace phpunittestproject\test;

use \phpunittestproject\src\Foo;


/**
 * 
 * Baz Test
 * 
 * This test class utilizes dataProvider methods to feed
 * test methods. The dataProvider methods are not annotated
 * with codeCoverageIgnore.
 *
 */
class BazTest extends \PHPUnit\Framework\TestCase
{


    /**
     * 
     * 
     */
    public function providerTestSetDateWithInvalidData()
    {
        return array(
                array(new \DateTime('1990-01-01 11:11:11')),
        );
    }

    /**
     *
     * 
     */
    public function providerTestSetDateWithValidData()
    {
        return array(
                array(new \DateTime('2011-01-01 11:11:11')),
        );
    }

    /**
     *
     * 
     */
    public function providerTestSetNameWithInvalidData()
    {
        return array(
                array('Bad Name 666'),              
        );
    }

    /**
     *
     * 
     */
    public function providerTestSetNameWithValidData()
    {
        return array(
                array('Good Name'),
        );
    }

    /**
     * 
     * 
     */
    public function testGetDate()
    {
        $foo = new Foo();
        $date = new \DateTime('2001-01-01 11:11:11');
        $foo->setDate($date);
        $this->assertEquals($date, $foo->getDate());
    }

    /**
     *
     * 
     */
    public function testGetName()
    {
        $foo = new Foo();
        $foo->setName('A Good Name');
        $this->assertEquals('A Good Name', $foo->getName());
    }

    /**
     *
     * @dataProvider providerTestSetDateWithInvalidData
     * 
     * 
     */
    public function testSetDateWithInvalidData($value)
    {
        $foo = new Foo();
        $foo->setDate($value);
        $this->assertAttributeNotEquals($value, 'date', $foo);
    }

    /**
     *
     * @dataProvider providerTestSetDateWithValidData
     *
     *
     */
    public function testSetDateWithValidData($value)
    {
        $foo = new Foo();
        $foo->setDate($value);
        $this->assertAttributeEquals($value, 'date', $foo);
    }

    /**
     *
     * @dataProvider providerTestSetNameWithInvalidData
     * 
     * 
     */
    public function testSetNameWithInvalidData($value)
    {
        $foo = new Foo();
        $foo->setName($value);
        $this->assertAttributeNotEquals($value, 'name', $foo);
    }

    /**
     *
     * @dataProvider providerTestSetNameWithValidData
     *
     *
     */
    public function testSetNameWithValidData($value)
    {
        $foo = new Foo();
        $foo->setName($value);
        $this->assertAttributeEquals($value, 'name', $foo);
    }

}

phpunit.xml:

<?xml version="1.0" encoding="UTF-8"?>
<phpunit colors="true" bootstrap="./vendor/autoload.php">
    <testsuites>
        <testsuite name="DataProviderTestSuite">
            <file>phpunittestproject/test/FooTest.php</file>
            <file>phpunittestproject/test/BarTest.php</file>
            <file>phpunittestproject/test/BazTest.php</file>
        </testsuite>
    </testsuites>
    <filter>
        <whitelist>
            <file>phpunittestproject/test/FooTest.php</file>
            <file>phpunittestproject/test/BarTest.php</file>
            <file>phpunittestproject/test/BazTest.php</file>
        </whitelist>
    </filter>
</phpunit>

1 个答案:

答案 0 :(得分:0)

感谢@Christopher的解决方案!错误配置的phpunit.xml文件导致PhpUnit测试测试。编辑白名单元素修复了低代码覆盖率分数。 dataProvider方法不需要使用@codeCoverageIgnore注释!

<?xml version="1.0" encoding="UTF-8"?>
<phpunit colors="true" bootstrap="./vendor/autoload.php">
    <testsuites>
        <testsuite name="DataProviderTestSuite">
            <!-- Test files go here: -->
            <file>phpunittestproject/test/FooTest.php</file>
            <file>phpunittestproject/test/BarTest.php</file>
            <file>phpunittestproject/test/BazTest.php</file>
        </testsuite>
    </testsuites>
    <filter>
        <whitelist>
            <!-- Source files to be tested go here: -->
            <file>phpunittestproject/src/Foo.php</file>
        </whitelist>
    </filter>
</phpunit>

Code coverage HTML report with 100% score.