2012-05-03 69 views
3

如果兩個或多個測試類(測試相同接口/抽象類的不同實現)具有不同固定裝置的共同測試但是,那麼重構測試用例是否是一個好主意?將常見測試重構爲基本測試用例

比方說,代碼和測試看起來像這樣:

interface MathOperation 
{ 
    public function doMath($a, $b); 
} 

class Sumator implements MathOperation 
{ 
    public function doMath($a, $b) 
    { 
     return $a + $b; 
    } 
} 


class Multiplicator implements MathOperation 
{ 
    public function doMath($a, $b) 
    { 
     return $a * $b; 
    } 
} 

// tests 
class SumatorTest extends PHPUnit_Framework_TestCase 
{ 
    /** 
    * @var Sumator 
    */ 
    protected $sumator; 

    public function setUp() 
    { 
     $this->sumator = new Sumator; 
    } 

    /** 
    * @dataProvider fixtures 
    */ 
    public function testDoMath($a, $b, $expected) 
    { 
     $result = $this->sumator->doMath($a, $b); 
     $this->assertEqual($expected, $result); 
    } 

    public function fixtures() 
    { 
     return array(
      array(1, 1, 2); 
      array(2, 1, 3); 
      array(100, -1, 99); 
     ); 
    } 
} 

class MultiplicatorTest extends PHPUnit_Framework_TestCase 
{ 
    /** 
    * @var Multiplicator 
    */ 
    protected $multiplicator; 

    public function setUp() 
    { 
     $this->multiplicator = new Multiplicator; 
    } 

    /** 
    * @dataProvider fixtures 
    */ 
    public function testDoMath($a, $b, $expected) 
    { 
     $result = $this->multiplicator->doMath($a, $b); 
     $this->assertEqual($expected, $result); 
    } 

    public function fixtures() 
    { 
     return array(
      array(1, 1, 1); 
      array(2, 1, 2); 
      array(100, -1, -100); 
     ); 
    } 
} 

,我希望他們(測試)看起來像:

class MathOperationTestCase extends PHPUnit_Framework_TestCase 
{ 
    /** 
    * @var MathOperation 
    */ 
    protected $operation; 

    public function setUp() 
    { 
     $this->operation = $this->createImpl(); 
    } 

    /** 
    * @return MathOperation 
    */ 
    abstract function createImpl(); 

    /** 
    * @dataProvider fixtures 
    */ 
    public function testDoMath($a, $b, $expected) 
    { 
     $result = $this->operation->doMath($a, $b); 
     $this->assertEqual($expected, $result); 
    } 

    abstract public function fixtures(); 
} 

class SumatorTest extends MathOperationTestCase 
{ 
    public function createImpl() 
    { 
     return new Sumator; 
    } 

    public function fixtures() 
    { 
     return array(
      array(1, 1, 2); 
      array(2, 1, 3); 
      array(100, -1, 99); 
     ); 
    } 
} 

class MultiplicatorTest extends MathOperationTestCase 
{ 
    public function createImpl() 
    { 
     return new Multiplicator; 
    } 

    public function fixtures() 
    { 
     return array(
      array(1, 1, 1); 
      array(2, 1, 2); 
      array(100, -1, -100); 
     ); 
    } 
} 

這似乎更好地組織,但可能缺乏可讀性。所以最後我不確定它是否可用。

回答

1

經過一番考慮,我得出的結論是隻有這種方法的優點是減少代碼重複。

提取基測試用例只能應用於被測類的常用接口,但這些接口不能強制我們嘗試測試的業務邏輯的工作流相同。讓我們修改Multiplicator類來證明這一點。

class Multiplicator implements MathOperation 
{ 
    private $factor; // added factor which influences result of doMath() 

    public function __construct($factor) 
    { 
     $this->factor = $factor; 
    } 

    public function doMath($a, $b) 
    { 
     return ($a * $b) * $factor; 
    } 
} 

現在,雖然SumatorMultiplicator共享同一接口,順便Multiplicator應測試是完全不同的例如

class MultiplicatorTest extends MathOperationTestCase 
{ 
    // rest of code 

    public function testDoMath2($ab, $b, $factor, $expected) 
    { 
     $multiplicator = new Multiplicator($factor); 
     $result = $multiplicator->doMath($a, $b); 
     $this->assertEqual($expected, $result); 
    } 
} 

另外我將不得不通過測試類的輕微的修改,以保持與基地測試用例向後兼容性這是巨大的禁忌...

class Multiplicator implements MathOperation 
{ 
    // rest of code 

    public function __construct($factor = 1) // default value set in class 
    { 
     $this->factor = $factor; 
    } 
} 

...或通過進行自我測試。這使得測試源自提取的測試用例,反覆出現,而且毫無用處。

class MultiplicatorTest extends MathOperationTestCase 
{ 
    // rest of code 

    public function createImpl() 
    { 
     return new Multiplicator(1); // added default value 
    } 
} 

除了明顯的缺陷外,上述所有內容都增加了可讀性和可維護性方面的不必要的複雜性。

感謝大家的貢獻。

1

如果您的原始代碼發生變化,測試也必須更改。記住這一點,然後您會看到哪種方式可以更輕鬆地處理更改。 如果您決定在將來分開接口或者像這樣的問題可能會幫助您做出決定會怎麼樣。

1

您已經將PHPUnitTest的功能抽象出來,足以使其適用於多個類。涼。我還發現,如果將來Sumator或Multiplicator增加了功能,這會變得有問題 - 無論您對任何一個課程做什麼,您都會遇到一個問題,即您是否應該將其抽象出來在測試框架中的類也是如此。

這使我在腦海中的可維護性變得更加複雜,並不是因爲您必須調整多個類(這對於測試類來說都是這樣),而是因爲維護附加代碼結構的額外負擔,您需要隨時跟蹤選擇任一課程。

單位測試,在我看來,適用於一對一的結構由於這個原因。你的方法減少了代碼重複,只要一個類具有相同的結構和功能,它就適用於這個測試類。另一方面,在我看來,它打開了使課堂適合測試的誘惑,而不是相反。

0

我發現,具有測試一個基類主要是唯一有用的在兩種情況下:

  1. 當基類僅包含的東西像你正在使用的應用程序常用的實用工具/輔助方法/類,即普通的模擬課程創作者。
  2. 被測產品與其他產品共享一些代碼,但有所延伸;因此您可以在測試基類和它的子項中反映這一點。