2012-05-03 4 views
3

同じインタフェース/抽象クラスの異なる実装をテストする2つ以上のテストクラスに共通のテストがある場合異なるフィクスチャでテストケースをリファクタリングすることをお勧めしますか?共通テストをベーステストケースにリファクタリングする

コードとテストは次のようになりとしましょう:

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のいずれかが将来機能を追加した場合、これが問題になることもわかります。どちらのクラスにも関係なく、常にベースに抽象化する必要があるかどうかという疑問に直面しますテストフレームワークのクラスでもあります。

これは、複数のクラス(テストクラスでいずれかの方法で発生します)を調整する必要があるのではなく、いつでも変更する必要がある追加のコード構造を維持する負担が増すためです。いずれかのクラスの選択肢を作る。

ユニットテストは、私の考えでは、このような理由から1対1構造で適用されます。あなたのメソッドは、クラスが同じ構造と機能を持つ限り、このテストクラスに適用できるという意味でコードの重複を減らします。一方、私の心の中では、それは、クラスをテストに合うように誘惑することになります。

0

は、私はテストのための基本クラスを有する2つのケースで主にのみ有用であることを見つける:基底クラスが単独で作業しているアプリケーションのための共通のユーティリティ/ヘルパー・メソッド/クラスのようなものが含まれてい

  1. 、すなわち一般的なモッククラスの作成者。
  2. テスト対象製品が他の製品といくつかのコードを共有していますが、いくらか拡張されています。したがって、テストベースクラスとその子でこれをミラーリングします。
関連する問題