web-dev-qa-db-ja.com

ユニットテストのためにモックオブジェクトをクラスに渡す方法

モックされたオブジェクトをユニットテストのためにクラスに渡す方法はたくさんあるようですが、私のPHPアプリケーションでどのアプローチを取るのが適切かはわかりません。

Dependency Injectionを使用している場合、テストからモックを渡すために使用できるコンストラクター引数として依存関係を持つことができます。

class Example
{
    private $dependency;

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

ただし、Dependency Injectionを使用していないため、この方法は使用できません。

代わりに、コンストラクター内から呼び出されるプライベートプロパティを設定するパブリックメソッドを追加できます。

class Example
{
    private $dependency;

    public function __construct()
    {
        $this->setDependency(new Dependency());
    }

    public function setDependency(Dependency $dependency)
    {
        $this->dependency = $dependency;
    }
}

テストでこのパブリックメソッドを使用して、依存関係をモック値に設定できます。
これは最良のアプローチのようです、ただし、テストを簡単にするためにこの新しいsetDependencyメソッドを追加しましたが、少し汚い感じがします。

または、リフレクションを使用してprotectedプロパティの値を直接設定することもできますが、テストではExampleクラスの実装に関する知識が必要です。

class Example
{
    protected $dependency;
}

class ExampleTest
{
    public function testDependency()
    {
        $example = new Example();

        $dependencyMock = $this->getMock(); // Truncated

        $this->setPrivateProperty($example, "dependency", $dependencyMock);
    }
}

依存性注入を使用していないときに、モックしたオブジェクトをExampleクラスにテストで渡すための正しい方法は何ですか?

1
nevada_scout

ただし、Dependency Injectionを使用していないため、この方法は使用できません。

していませんか?あなたがこのようなコードを書くとき:

class Example
{
    private $dependency;

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

それは悲鳴 依存性注入 を私に(上の最初のExampleは準備完了です コンストラクター注入 )。結局のところ、依存性注入は パラメーターを渡すstate の一部にするためのファンシーな用語にすぎません。 DIコンテナフレームワークまたはライブラリ を使用して新しい言語に相当するDIを実行したい人もいれば、言語に組み込まれたツールを使い続けて Pure DI を使用したい人もいます。 。上記で提供したのはPure DIの例です。

どちらの方法でも、主なポイントは、構成コードを動作コードから分離することです。そうする限り、DIの主な利点、つまり、多くの書き換えをせずに再構成できる機能を利用できます。

純粋なDIを使用するための単純なパターンは、メイン(または可能な限りコールスタックまで)で作成(構築)することです。しかし in PHP mainはコードの最初の行にすぎません なので、次のようになっていると仮定します。

$example = new Example( new Dependency() );

あなたの注射があります。 DIコンテナーは必要ありません。より多くのオブジェクトに対してより多くの注入がある場合は、それらもここに配置します。これを行うと、接続されたオブジェクトグラフを構築できます。 Dependencyを変更して他の要素に依存する必要がある場合は、ここでもビルドします。それらを与えるかどうか$名前はあなた次第です。最も読みやすいものは何でも。ただ くだらないこと1。名前はいいことです。

すべてが構成/構築されたら、オブジェクトグラフの動作を開始するための動作コードを1行と1行だけ記述します。何かのようなもの:

$example.start();

これで手続き型コードの終わりです。それ以外はすべてオブジェクト指向です。

あなたも提供しました:

class Example
{
    private $dependency;

    public function __construct()
    {
        $this->setDependency(new Dependency());
    }

    public function setDependency(Dependency $dependency)
    {
        $this->dependency = $dependency;
    }
}

テストでこのパブリックメソッドを使用して、依存関係をモック値に設定できます。これは最良のアプローチのようですが、テストを簡単にするためにこの新しいsetDependencyメソッドを追加しました。

これが汚いのは、テストで不要な場合にのみ、テストのためにクラスが mutable (作成後に状態が変わる可能性がある)に強制されることです。テストは最初の例で完全に満足しました。

しかし、あなたは今までになかった何かをここに持っています。ここにあるのは、既知のデフォルト値です。明らかなデフォルトがある場合にすべてを構成しなければならないのは、大変なことです。 Convention over configuration は、単に構成コードを変更するのではなく、構成コードを追加することで上書きできる既知の適切なデフォルトの規則を使用してシステムを設計する方が良いと主張しています。

オブジェクトを強制的に変更せずに、必要なすべてのものをテスト、規約、および構成する方法があります。

class Example
{
    private $dependency;

    public function __construct( Dependency $dependency = new Dependency() )
    {
        $this->dependency = $dependency;
    }
}

オプションの引数 を使用します2。これで、規則が必要なときに使用できます。

$example = new Example();

それはあなたが使用した場合と同じです

$example = new Example( new Dependency() );

まだあなたは自由に書くことができます

$example = new Example( new DependencyMock() );

テストで使用するか、コードで使用して、慣習から離れて構成します。

はい、技術的には規約はハードコーディングされています。ただし、ハードコードされた値はオーバーライド可能なデフォルト値です。それは違いを生む世界です。ファクトリーメソッドを使用する必要がある場合、これと同じトリックがそれらでも機能します。

最後に、これで私たちを襲った:

class Example
{
    protected $dependency;
}

次に、テストでのリフレクションの使用について話し始めます。

依存性注入を使用していないときに、モックされたオブジェクトをサンプルクラスに渡すための正しい方法は何ですか?

正しい方法は、注射を受け付けるように書き換えることです。上に示したように、これは特別なツールを必要としません。リフレクションを使用してカプセル化の保護を無視することもできますが、通常は parameterize を使用し、夜の暗闇で窓を壊すのではなく、正面玄関から注入するようにします。

4
candied_orange