2017-04-04 254 views
1

我一直在使用PhpUnit,結果很好,只用斷言來測試我的代碼。最近我決定嘗試使用覆蓋報告分析的PhpUnit,但我注意到使用dataProvider方法的測試往往會降低我的代碼覆蓋率。我想知道我可能會做錯什麼,或者這是否是dataProvider測試技術的後果?我正在使用PhpUnit 6和Php 7.爲什麼PhpUnit數據提供商降低覆蓋率分數?

我已經包含了一個源類Foo,下面有三個測試類來測試它。 FooTest使用常規測試方法,不使用數據提供程序。 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

我認爲你需要從白名單中刪除測試文件。你不會在你自己的測試中做代碼覆蓋,它的意思是告訴你你的測試覆蓋了哪些應用程序代碼。 – Christopher

+1

解決了它克里斯托弗謝謝!我編輯了phpunit.xml文件,以便將白名單文件元素替換爲單個元素' phpunittestproject/src/Foo.php'。好棒! – peej

回答

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.