Magento 2のPHPUnit 4.1でPHP7コードを単体テストする適切な方法は何ですか?


23

モジュールを作成するとき、アプリケーションの最も重要な部分の単体テストをモジュールに提供しようとしています。ただし、現時点(Magento 2.1.3)では、単体テストの作成方法についていくつかの方法があります。

さまざまなテスト方法

  • それを統合し、bin/magento dev:tests:run unitMagentoにバンドルされているデフォルトのphpunit設定の上で実行します。
  • それらを別々にvendor/bin/phpunit app/code/Vendor/Module/Test/Unit記述し、実行して、Magentoであるすべてをモックします。
  • それらを個別に記述し、すべてをモックし、PHPUnitのシステムグローバルバージョンを使用します。
  • それらを別々に記述し、で実行しますがvendor/bin/phpunit、それでも利用し\Magento\Framework\TestFramework\Unit\Helper\ObjectManagerます。

Magento 2およびPHPUnit

それに加えて、Magento 2にはPHPUnit 4.1.0がバンドルされており、PHP7互換ではありません。ネイティブ型(stringおよびintなど)を型ヒントし、署名で戻り値の型を宣言すると、エラーがスローされます。たとえば、次のようなメソッドシグネチャを持つインターフェイス/クラス:

public function foo(string $bar) : bool;

... PHPUnit 4.1.0でモックすることはできません。:-(

私の現在の状況

このため、私はほとんどの場合、3番目の方法(システムグローバルなPHPUnitバージョンを呼び出すこと)で単体テストを書いています。

私の設定では、PHPUnit 5.6がグローバルにインストールされているため、適切なPHP7コードを記述して解決できますが、いくつかの調整が必要です。例えば:

phpunit.xml コンポーザーオートローダーを使用できるように、次のようにする必要があります。

<?xml version="1.0"?>
<phpunit bootstrap="../../../../../../vendor/autoload.php"
         colors="true">
    <testsuites>
        <testsuite name="Testsuite">
            <directory>.</directory>
        </testsuite>
    </testsuites>
</phpunit>

...そして、私のすべての- setUp()メソッドでは、次のチェックを行っているので、前方互換性でテストを書くことができます:

// Only allow PHPUnit 5.x:
if (version_compare(\PHPUnit_Runner_Version::id(), '5', '<')) {
    $this->markTestSkipped();
}

このように、Magentosの組み込みPHPUnitでテストを実行すると、エラーはスローされません。

私の質問

だからここに私の質問があります:これは単体テストを書く「健康な」方法ですか?なぜなら、Magentoにはテストを支援するためのツールが多数バンドルされているのは正しくないと思われ、PHP7を使用しているため使用できません。この問題に対処するGitHubのチケットがあることは知っていますが、コミュニティが現在どのようにテストを書いているのでしょうか。

Magento 2でユニットテストを書く方法はありますか?コードを「ダウングレード」する必要はなく、Magentoのビルトインヘルパーを使用してオブジェクトマネージャーが触れるすべてをモックできますか?または、単体テストでもオブジェクトマネージャーを使用するのは悪い習慣ですか?

独自のカスタムモジュールを単体テストする方法に関する適切な方法について、多くのガイダンス/例が不足しています。


1
なんて素晴らしい質問でしょう。
カムディクソン

回答:


17

バンドルされているPHPUnitバージョンを使用することは、たとえ古くてもCIですべてのモジュールのテストを同時に実行できるため、おそらく最善の方法です。

バンドルされているテストフレームワークと互換性のない方法でテストを記述すると、テストの価値が大幅に低下すると思います。
もちろん、異なるバージョンのPHPUnitでテストを実行するようにCIをセットアップすることもできますが、これによりビルドシステムが非常に複雑になります。

そうは言っても、PHP 5.6をサポートする価値はないということに同意します。PHP7のスカラー型のヒントを使用し、可能な限り型のヒントを返します(さらに、市場のことは気にしません)。

PHPUnit 4.1モックライブラリの制限を回避するために、過去に使用した少なくとも2つのかなり簡単な回避策があります。

  1. 匿名または通常のクラスを使用して、テストダブルを構築します。たとえば、

    $fooIsFalseStub = new class extends Foo implements BarInterface() {
        public function __construct(){};
        public function isSomethingTrue(string $something): bool
        {
            return false;
        }
    };
  2. バンドルされたPHPUnitを使用しますが、require-devたとえばhttps://github.com/padraic/mockeryなどのコンポーザー経由で含めることができるサードパーティのモックライブラリを使用し ます。私が試したすべてのモックライブラリは、4.1などの非常に古いバージョンのPHPUnitでさえ、テストフレームワークで非常に簡単に使用できます。

これらのどちらも、他のものよりも技術的な利点はありません。どちらでも必要なテストダブルロジックを実装できます。

個人的には、匿名クラスを使用することを好みます。これは、外部依存関係の数を増やすことはなく、また、そのように記述するほうが楽しいからです。

編集
質問に答えるには:

MockeryはPHPUnit 4.1.0がPHP7型のヒントを適切に処理できないという問題を「解決」しますか?

はい、以下の例を参照してください。

また、匿名クラスのモックに対する利点は何ですか?

匿名クラスを使用してテストダブルを作成することも「モック」であり、PHPUnitsやMockeryなどのモックライブラリを使用することと実際には違いはありません。
モックは、作成方法に関係なく、特定のタイプのテストdoubleにのみ基づいています。
匿名クラスまたはモックライブラリの使用の1つの小さな違いは、匿名クラスは単なるPHPであるため、外部ライブラリの依存関係がないことです。それ以外の場合、利点も欠点もありません。それは単に好みの問題です。テストはテストフレームワークやモックライブラリに関するものではなく、テストはテスト対象のシステムを実行し、動作を自動的に検証するコードを記述するだけであることを示しているため、気に入っています。

また、メインのcomposer.jsonファイルのPHPUnitバージョンを5.3.5(PHP7をサポートし、パブリックモックメソッド(Magento 2のテストで必要)をサポートする最新バージョン)に更新するのはどうですか?

他のモジュールおよびコアのテストはPHPUnit 4.1でのみテストされるため、これは問題になる可能性があります。そのため、CIで誤ったエラーが発生する可能性があります。そのため、バンドル版のPHPUnitを使用することをお勧めします。@maksekは、PHPUnitを更新すると述べたが、そのためのETAはない。


Mockeryライブラリを使用して、PHPUnit 4.1でPHP7を実行する必要があるクラスのテストダブルを使用したテストの例:

<?php

declare(strict_types = 1);

namespace Example\Php7\Test\Unit;

// Foo is a class that will not work with the mocking library bundled with PHPUnit 4.1 
// The test below creates a mock of this class using mockery and uses it in a test run by PHPUnit 4.1
class Foo
{
    public function isSomethingTrue(string $baz): bool
    {
        return 'something' === $baz; 
    }
}

// This is another class that uses PHP7 scalar argument types and a return type.
// It is the system under test in the example test below.
class Bar
{
    private $foo;

    public function __construct(Foo $foo)
    {
        $this->foo = $foo;
    }

    public function useFooWith(string $s): bool
    {
        return $this->foo->isSomethingTrue($s);
    }
}

// This is an example test that runs with PHPUnit 4.1 and uses mockery to create a test double
// of a class that is only compatible with PHP7 and younger.
class MockWithReturnTypeTest extends \PHPUnit_Framework_TestCase
{
    protected function tearDown()
    {
        \Mockery::close();
    }

    public function testPHPUnitVersion()
    {
        // FYI to show this test runs with PHPUnit 4.1
        $this->assertSame('4.1.0', \PHPUnit_Runner_Version::id());
    }

    public function testPhpVersion()
    {
        // FYI this test runs with PHP7
        $this->assertSame('7.0.15', \PHP_VERSION);
    }

    // Some nonsensical example test using a mock that has methods with
    // scalar argument types and PHP7 return types.
    public function testBarUsesFoo()
    {
        $stubFoo = \Mockery::mock(Foo::class);
        $stubFoo->shouldReceive('isSomethingTrue')->with('faz')->andReturn(false);
        $this->assertFalse((new Bar($stubFoo))->useFooWith('faz'));
    }
}

Mockeryは、PHPUnit 4.1.0がPHP7タイプヒンティングを適切に処理できないという問題を「解決」しますか?また、匿名クラスのモックに対する利点は何ですか?また、メインcomposer.jsonファイルのPHPUnitバージョンを5.3.5(PHP7をサポートし、パブリックモックメソッド(Magento 2のテストで必要)をサポートする最新バージョン)に更新するのはどうですか?だから、より多くの質問になりました...
Giel Berkers

あなたの質問に対する回答を更新しました@GielBerkers
Vinai

すばらしい回答をありがとう。今では完全に明確です!Mockeryを試してみようと思います。匿名クラスは、Mockeryが既に提供している多くのことを再発明する必要があるようです。私は最初にPHPUnitの基本を学び、そこから先に進みたいと思いました。今がその時だと思う。
ゲルバーカーズ

すばらしいです!すばらしい図書館であるMockeryの探索をお楽しみください。あなたがそれをしている間に、多分、hamcrest、またアサーションライブラリをチェックしてください-それはMockeryとともに自動的にインストールされます。
ビナイ

3

現在、Magento 2は次のPHPバージョンをサポートしています。

"php": "~5.6.5|7.0.2|7.0.4|~7.0.6"

つまり、Magento Teamによって記述されたすべてのコードは、サポートされているすべてのバージョンで動作します。

したがって、Magento TeamはPHP 7のみの機能を使用しません。PHP 5.6の機能は、PHPUnit 4.1.0でカバーできます。

独自のコードを書くと、必要なすべてのことができ、好きな方法でテストを書くことができます。ただし、要件違反のため、Magento Marketplaceで拡張機能を公開することはできません。


実際、PHPUnit 5.7はPHP 5.6、PHP 7.0、およびPHP 7.1でサポートされています。PHPUnit 4.8は、PHP 5.3〜5.6でサポートされていました。そのため、Magento 2はPHP 5.6をサポートしていますが、それでもPHPUnit 5.7にアップグレードできます。
ビナイ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.