4

これが私が単体テストしているクラスです。現在、私はdoSomething機能をテストしています:

class FooClass {
  public function doSomething( $user ) {
    $conn = $this->getUniqueConnection( $user->id );
    $conn->doSomethingDestructive();
  }

  private function getUniqueConnection( $id ) {
    return new UniqueConnection( $id );
  }
}

ご覧のとおり、関数は、受け取った引数のプロパティに基づいて (ここではテストしていないクラス)doSomethingの新しいインスタンスを取得します。UniqueConnection問題は、UniqueConnection:: doSomethingDestructiveメソッドが破壊的であるため、テスト中に呼び出すことができないことです。UniqueConnectionしたがって、実際のものを使用するのではなく、スタブ/モックしたいと思います。

モックを注入する方法がわかりませんUniqueConnectionUniqueConnectionコンストラクターの引数を作成FooClassしますが、ご覧のとおり、関数のパラメーターに基づいて新しいコンストラクターが作成され、doSomething呼び出される可能性のある一意の ID はすべて事前にわかりません。

私が見ることができる唯一のオプションは、それ自体ではFooClassなくモックをテストするFooClassことです。getUniqueConnection次に、関数をモック/スタブを返す関数に置き換えます。これはモックをテストするのは悪いようですが、それ以外の方法で目的を達成する方法がわかりません。 UniqueConnectionはサード パーティ ベンダーのライブラリであり、変更できません。

4

5 に答える 5

4

UniqueConnectionFactoryを作成し、そのインスタンスを FooClass に渡すことができます。次に、あなたは持っています

  private function getUniqueConnection( $id ) {
    return $this->uniqueConnectionFactory->create( $id );
  }

一般に、これはファクトリーを使用する利点の 1 つです。newオペレーターをクラスから除外することで、作成されるオブジェクトをより簡単に変更できるようになります。

于 2012-08-13T23:47:28.693 に答える
2

時間をかけてコードをリファクタリングし、rambo coder が推奨するようにファクトリを使用できるようになるまでは、部分モックを使用して非破壊的な一意の接続を返すことができます。このような状況に陥った場合、通常は、テスト対象のクラスに複数の責任があることを意味します。

function testSomething() {
    $mockConn = $this->getMock('UniqueConnection');
    $mockConn->expects($this->once())
             ->method('doSomethingDestructive')
             ->will(...);
    $mockFoo = $this->getMock('FooClass', array('getUniqueConnection'));
    $mockFoo->expects($this->once())
            ->method('getUniqueConnection')
            ->will($this->returnValue($mockConn));
    $mockFoo->doSomething();
}
于 2012-08-14T08:46:36.287 に答える
2

Rambo Coder が言ったように、クラスでやりすぎることが問題です。特に、特定の1 つのクラスのインスタンスしか作成しない場合は、Factory を作成したいとは思いません。最も簡単な解決策は、UniqueConnection を作成する責任を逆にすることです。

<?php
class FooClass {
  public function doSomething( UniqueConnection $connection ) {
    $connection->doSomethingDestructive( );
  }
}

テスト中にモックを渡しnew UniqueConnection( $user->id )、実際のコードでa を渡します。

于 2012-08-14T08:51:54.167 に答える
0

さまざまな実行モードをサポートできるようにクラスを作成することは、場合によっては非常に重要です。これらのケースの1つは、あなたが求めているものです。

さまざまなモードをサポートするクラスを作成します。例えば

Class Connection {
    private $mode;

    public function setMode($mode) {
         $this -> $mode = $mode;
    }
}

これで、実行モードdoSomethingDestructiveに従って行動できます。

public function doSomethingDestructive() {
    if($this -> mode === "test") { //if we are in a test mode scenario
        //Log something
        // Or just do some logging and give a message
    } else {
        // do what it was suppose to do
    }
}

次回、クラスをテストするとき、その破壊的な関数が誤って何かを破壊することを心配する必要はありません。

  public function doSomething( $user ) {
    $conn = $this->getUniqueConnection( $user->id );
    $conn -> setMode("test"); //Now we are safe
    $conn->doSomethingDestructive(); //But the Testing is still being Ran
  }
于 2012-08-13T23:54:45.460 に答える
0

この場合、必要なのはモック オブジェクトではなく、テスト用のサブクラスです。メソッドに$conn->doSomethingDestructive();分割し、サブクラスFooClassとしてサブクラスTestFooClass化し、サブクラスで新しいメソッドをオーバーライドします。次に、望ましくない破壊的な動作を取得することなく、サブクラスを使用してテストできます。

例えば:

class FooClass {
  public function doSomething( $user ) {
    $conn = $this->getUniqueConnection( $user->id );
    $this->connDoSomethingDestructive($conn);
  }

  protected function connDoSomethingDestructive($conn) {
    $conn->doSomethingDestructive();
  }

  private function getUniqueConnection( $id ) {
    return new UniqueConnection( $id );
  }
}

class TestFooClass extends FooClass {
  protected function connDoSomethingDestructive() {

  }

  private function getUniqueConnection( $id ) {
    return new MockUniqueConnection( $id );
  }
}
于 2012-08-14T00:02:23.317 に答える