2011-11-07 19 views
15

PHPUnitを使用して抽象クラスで具象メソッドをモックする良い方法はありますか?phpunitを使用した抽象クラスの具体的なメソッドのモック

私がこれまでに発見したことはある:

  • を期待() - >意志()は、それが具体的な方法では動作しません。抽象メソッド
  • を使用して正常に動作します。代わりに元のメソッドが実行されます。
  • mockbuilderを使用して、すべての抽象メソッドとsetMethods()の具体的なメソッドを提供します。ただし、すべての抽象メソッドを指定する必要があり、テストが壊れやすく冗長すぎます。
  • MockBuilder :: getMockForAbstractClass()はsetMethod()を無視します。


は、ここで上記の点をexamplifyingいくつかのユニットテストです:

abstract class AbstractClass { 
    public function concreteMethod() { 
     return $this->abstractMethod(); 
    } 

    public abstract function abstractMethod(); 
} 


class AbstractClassTest extends PHPUnit_Framework_TestCase { 
    /** 
    * This works for abstract methods. 
    */ 
    public function testAbstractMethod() { 
     $stub = $this->getMockForAbstractClass('AbstractClass'); 
     $stub->expects($this->any()) 
       ->method('abstractMethod') 
       ->will($this->returnValue(2)); 

     $this->assertSame(2, $stub->concreteMethod()); // Succeeds 
    } 

    /** 
    * Ideally, I would like this to work for concrete methods too. 
    */ 
    public function testConcreteMethod() { 
     $stub = $this->getMockForAbstractClass('AbstractClass'); 
     $stub->expects($this->any()) 
       ->method('concreteMethod') 
       ->will($this->returnValue(2)); 

     $this->assertSame(2, $stub->concreteMethod()); // Fails, concreteMethod returns NULL 
    } 

    /** 
    * One way to mock the concrete method, is to use the mock builder, 
    * and set the methods to mock. 
    * 
    * The downside of doing it this way, is that all abstract methods 
    * must be specified in the setMethods() call. If you add a new abstract 
    * method, all your existing unit tests will fail. 
    */ 
    public function testConcreteMethod__mockBuilder_getMock() { 
     $stub = $this->getMockBuilder('AbstractClass') 
       ->setMethods(array('concreteMethod', 'abstractMethod')) 
       ->getMock(); 
     $stub->expects($this->any()) 
       ->method('concreteMethod') 
       ->will($this->returnValue(2)); 

     $this->assertSame(2, $stub->concreteMethod()); // Succeeds 
    } 

    /** 
    * Similar to above, but using getMockForAbstractClass(). 
    * Apparently, setMethods() is ignored by getMockForAbstractClass() 
    */ 
    public function testConcreteMethod__mockBuilder_getMockForAbstractClass() { 
     $stub = $this->getMockBuilder('AbstractClass') 
       ->setMethods(array('concreteMethod')) 
       ->getMockForAbstractClass(); 
     $stub->expects($this->any()) 
       ->method('concreteMethod') 
       ->will($this->returnValue(2)); 

     $this->assertSame(2, $stub->concreteMethod()); // Fails, concreteMethod returns NULL 
    } 
} 
+0

彼らは抽象的であるとして、あなたは抽象クラスをテストする必要はありません。あるいは、抽象テストケースを作成したいのですか? – hakre

+0

抽象クラスは別のクラスの依存関係です。ですから、$ object-> concreteMethod()を使うSomeClass :: getMyCalculatedValue()をテストしたいと思います。 concreteMethod()は変更できるか、設定が難しいかもしれないので、concreteMethod()の戻り値を指定します。 – CheeseSucker

答えて

4

私はあなたがとにかくそれらすべてを模擬する必要があるため、すべての抽象メソッドを追加するために私のベースのテストケースにgetMock()をオーバーライドします。あなたはビルダーと似た何かを間違いなく行うことができます。

重要:プライベートメソッドをモックすることはできません。

public function getMock($originalClassName, $methods = array(), array $arguments = array(), $mockClassName = '', $callOriginalConstructor = TRUE, $callOriginalClone = TRUE, $callAutoload = TRUE) { 
    if ($methods !== null) { 
     $methods = array_unique(array_merge($methods, 
       self::getAbstractMethods($originalClassName, $callAutoload))); 
    } 
    return parent::getMock($originalClassName, $methods, $arguments, $mockClassName, $callOriginalConstructor, $callOriginalClone, $callAutoload); 
} 

/** 
* Returns an array containing the names of the abstract methods in <code>$class</code>. 
* 
* @param string $class name of the class 
* @return array zero or more abstract methods names 
*/ 
public static function getAbstractMethods($class, $autoload=true) { 
    $methods = array(); 
    if (class_exists($class, $autoload) || interface_exists($class, $autoload)) { 
     $reflector = new ReflectionClass($class); 
     foreach ($reflector->getMethods() as $method) { 
      if ($method->isAbstract()) { 
       $methods[] = $method->getName(); 
      } 
     } 
    } 
    return $methods; 
} 
+0

これは機能します。私はプライベートメソッドをモックする必要はありません=) – CheeseSucker

+0

これは 'getMockForAbstractClass'とどのように違うのですか? – FoolishSeth

+1

@FoolishSeth PHPUnitの以前のバージョンでは、[模擬するための具体的な具体的な方法の指定](https://github.com/sebastianbergmann/phpunit-mock-objects/pull/49)は許可されていませんでした。すべての抽象メソッドをモックすることなくモックオブジェクトをインスタンス化することはできないので、 'getMock()'などを呼び出すときにそれらをマージするのが妥当と思われます。 –

14

は2年前、このためにプル要求がありましたが、情報がドキュメントに追加されたことがない:https://github.com/sebastianbergmann/phpunit-mock-objects/pull/49

あなたはgetMockForAbstractClassの引数7で配列にあなたの具体的な方法を渡すことができます()。

はコードを参照してください:https://github.com/andreaswolf/phpunit-mock-objects/blob/30ee7452caaa09c46421379861b4128ef7d95e2f/PHPUnit/Framework/MockObject/Generator.php#L225

関連する問題