web-dev-qa-db-ja.com

注入可能なコードをテストする方法は?

そのため、システム全体で次のコードを使用しています。私たちは現在ユニットテストを遡及的に書いています(私の主張がなかったよりも遅くなっています)が、これがどのようにテスト可能であるかわかりませんか?

public function validate($value, Constraint $constraint)
{
    $searchEntity = EmailAlertToSearchAdapter::adapt($value);

    $queryBuilder = SearcherFactory::getSearchDirector($searchEntity->getKeywords());
    $adapter = new SearchEntityToQueryAdapter($queryBuilder, $searchEntity);
    $query = $adapter->setupBuilder()->build();

    $totalCount = $this->advertType->count($query);

    if ($totalCount >= self::MAXIMUM_MATCHING_ADS) {
        $this->context->addViolation(
            $constraint->message
        );
    }
}

概念的には、これはどの言語にも適用できるはずですが、私はPHPを使用しています。このコードは、Searchオブジェクトに基づいてElasticSearchクエリオブジェクトを作成するだけで、EmailAlertオブジェクトから作成されます。これらのSearchEmailAlertは単なるPOPOです。

私の問題は、SearcherFactory(静的メソッドを使用)や、SearcherFactory::getSearchDirectorからの結果を必要とするSearchEntityToQueryAdapterをモックアウトする方法がわからないことです。 andSearchインスタンス。メソッド内の結果から作成されたものを注入するにはどうすればよいですか?気づいていないデザインパターンがあるのでは?

助けてくれてありがとう!

13
iLikeBreakfast

いくつかの可能性があり、PHPでstaticメソッドをモックする方法があります。私が使用した最良の解決策は AspectMock ライブラリで、composer =(静的メソッドをモックする方法は、ドキュメントからかなり理解できます)。

ただし、これは別の方法で修正する必要がある問題の直前の修正です。

クエリの変換を担当するレイヤーを単体テストしたい場合は、非常に簡単な方法で行うことができます。

現在、validateメソッドはいくつかのクラスの一部であると想定しています。すべての静的呼び出しをインスタンス呼び出しに変換する必要がない非常に迅速な修正は、静的メソッドのプロキシとして機能するクラスを構築することです。これらのプロキシを、以前は静的メソッドを使用していたクラスに注入します。

class EmailAlertToSearchAdapterProxy
{
    public function adapt($value)
    {
        return EmailAlertToSearchAdapter::adapt($value);
    }
}

class SearcherFactoryProxy
{
    public function getSearchDirector(array $keywords)
    {
        return SearcherFactory::getSearchDirector($keywords);
    }
}

class ClassWithValidateMethod
{
    private $emailProxy;
    private $searcherProxy;

    public function __construct(
        EmailAlertToSearchAdapterProxy $emailProxy,
        SearcherFactoryProxy $searcherProxy
    )
    {
        $this->emailProxy = $emailProxy;
        $this->searcherProxy = $searcherProxy;
    }

    public function validate($value, Constraint $constraint)
    {
        $searchEntity = $this->emailProxy->adapt($value);

        $queryBuilder = $this->searcherProxy->getSearchDirector($searchEntity->getKeywords());
        $adapter = new SearchEntityToQueryAdapter($queryBuilder, $searchEntity);
        $query = $adapter->setupBuilder()->build();

        $totalCount = $this->advertType->count($query);

        if ($totalCount >= self::MAXIMUM_MATCHING_ADS) {
            $this->context->addViolation(
                $constraint->message
            );
        }
    }
}
11
Andy

まず、これを個別のメソッドに分割することをお勧めします。

public function validate($value, Constraint $constraint)
{
    $totalCount = QueryTotal($value);
    ShowMessageWhenTotalExceedsMaximum($totalCount,$constraint);
}

private function QueryTotal($value)
{
    $searchEntity = EmailAlertToSearchAdapter::adapt($value);

    $queryBuilder = SearcherFactory::getSearchDirector($searchEntity->getKeywords());
    $adapter = new SearchEntityToQueryAdapter($queryBuilder, $searchEntity);
    $query = $adapter->setupBuilder()->build();

    return $this->advertType->count($query);
}

private function ShowMessageWhenTotalExceedsMaximum($totalCount,$constraint)
{
    if ($totalCount >= self::MAXIMUM_MATCHING_ADS) {
        $this->context->addViolation(
            $constraint->message
        );
    }
}

これにより、これら2つの新しいメソッドをパブリックにして、QueryTotalShowMessageWhenTotalExceedsMaximumを個別にテストすることを検討できる状況になります。ここで実行可能なオプションは、実際にはElasticSearchのみをテストするため、実際にQueryTotalを単体テストするにはnotです。 ShowMessageWhenTotalExceedsMaximumの単体テストの記述は簡単で、ビジネスロジックを実際にテストするため、はるかに理にかなっています。

ただし、「validate」を直接テストする場合は、クエリ関数自体をパラメーターとして「validate」に渡すことを検討してください(デフォルト値は$this->QueryTotal)を使用すると、クエリ関数をモックアウトできます。 PHP構文が正しいかどうかわからないので、正しくない場合は、「疑似コード」と読んでください:

public function validate($value, Constraint $constraint, $queryFunc=$this->QueryTotal)
{
    $totalCount =  $queryFunc($value);
    ShowMessageWhenTotalExceedsMaximum($totalCount,$constraint);
}
4
Doc Brown