Magento 2:未定義のメソッドMock_BlockFactory_4b440480 :: create()テストの呼び出しの修正


7

私は最近、プル提出し、直接使用されているオブジェクトマネージャの単一のインスタンスを修正するためにMagentoのにリクエストを。

ただし、Magentoのtravisユニットテストの実行は次のエラーで失敗しました

PHPの致命的なエラー:/home/travis/build/magento/magento2/app/code/Magento/Cms/Controller/Adminhtml/Block/Delete.phpの39行目の未定義メソッドMock_BlockFactory_4b440480 :: create()への呼び出し

travisビルドに基づいて、どのテストが失敗したかさえわかりません。スタックトレースを使用して、ローカルで同様の(同一の)エラーをローカルで取得できました

PHP Fatal error:  Call to undefined method Mock_BlockFactory_ec77572c::create() in /Users/alanstorm/Documents/github/astorm/magento2/app/code/Magento/Cms/Controller/Adminhtml/Block/Delete.php on line 39
PHP Stack trace:
PHP   1. {main}() /Users/alanstorm/Documents/github/astorm/magento2/vendor/phpunit/phpunit/phpunit:0
PHP   2. PHPUnit_TextUI_Command::main() /Users/alanstorm/Documents/github/astorm/magento2/vendor/phpunit/phpunit/phpunit:55
PHP   3. PHPUnit_TextUI_Command->run() /Users/alanstorm/Documents/github/astorm/magento2/vendor/phpunit/phpunit/src/TextUI/Command.php:132
PHP   4. PHPUnit_TextUI_TestRunner->doRun() /Users/alanstorm/Documents/github/astorm/magento2/vendor/phpunit/phpunit/src/TextUI/Command.php:179
PHP   5. PHPUnit_Framework_TestSuite->run() /Users/alanstorm/Documents/github/astorm/magento2/vendor/phpunit/phpunit/src/TextUI/TestRunner.php:426
PHP   6. PHPUnit_Framework_TestSuite->run() /Users/alanstorm/Documents/github/astorm/magento2/vendor/phpunit/phpunit/src/Framework/TestSuite.php:675
PHP   7. PHPUnit_Framework_TestCase->run() /Users/alanstorm/Documents/github/astorm/magento2/vendor/phpunit/phpunit/src/Framework/TestSuite.php:675
PHP   8. PHPUnit_Framework_TestResult->run() /Users/alanstorm/Documents/github/astorm/magento2/vendor/phpunit/phpunit/src/Framework/TestCase.php:753
PHP   9. PHPUnit_Framework_TestCase->runBare() /Users/alanstorm/Documents/github/astorm/magento2/vendor/phpunit/phpunit/src/Framework/TestResult.php:686
PHP  10. PHPUnit_Framework_TestCase->runTest() /Users/alanstorm/Documents/github/astorm/magento2/vendor/phpunit/phpunit/src/Framework/TestCase.php:817
PHP  11. ReflectionMethod->invokeArgs() /Users/alanstorm/Documents/github/astorm/magento2/vendor/phpunit/phpunit/src/Framework/TestCase.php:951
PHP  12. Magento\Cms\Test\Unit\Controller\Adminhtml\Block\DeleteTest->testDeleteAction() /Users/alanstorm/Documents/github/astorm/magento2/vendor/phpunit/phpunit/src/Framework/TestCase.php:951
PHP  13. Magento\Cms\Controller\Adminhtml\Block\Delete->execute() /Users/alanstorm/Documents/github/astorm/magento2/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Block/DeleteTest.php:151

ローカルの障害をこのテストに絞り込むことができましたが、何が起こっているのかについて少し途方に暮れています。

私の推測では、テストフレームワークはDI引数を自動的にモック化しましたが、自動モックはcreateメソッドを欠いていると思います。その場合、私の実際の質問は、Magentoのテストフレームワークに新しく挿入された依存関係のモックを追加するにどうすればよいです

ただし、Magentoのテスト用のうさぎの穴を掘り下げたことは一度もないので、実際にここで何を行う必要があるのか​​わかりません。Magentoのテスト経験を持つ誰かが私を正直に設定できますか?


ここと同じように、ファクトリによって返されるオブジェクトをモックする必要があると思います。create
nevvermind 2016年

回答:


11

\Magento\Framework\TestFramework\Unit\Helper\ObjectManager自動的に工場出荷時のモックを作成することができません。
\Magento\Framework\TestFramework\Unit\Helper\ObjectManager余談ですが、ユニットテストでの魔法の量を最小限に抑えるために、私はを使用していません。)

テストに合格するには、次の変更が必要です。

最初に、セットアップでモデルブロックファクトリのモックを作成します。

$this->modelBlockFactoryMock = $this->getMockBuilder(\Magento\Cms\Model\BlockFactory::class)
    ->disableOriginalConstructor()
    ->setMethods(['create'])
    ->getMock();

または、よりコンパクト:

$this->modelBlockFactoryMock = $this->getMock(\Magento\Cms\Model\BlockFactory::class, ['create'], [], '', false);

次に、新しいモックをコントローラーのコンストラクター引数に追加します。

$this->deleteController = $this->objectManager->getObject(
    'Magento\Cms\Controller\Adminhtml\Block\Delete',
    [
        'context' => $this->contextMock,
        'modelBlockFactory' => $this->modelBlockFactoryMock
    ]
);

3番目にBlockモックオブジェクトマネージャーではなく新しいファクトリーによってモデルが作成されることを期待しないようにテストを更新します。

交換する

$this->objectManagerMock->expects($this->once())
    ->method('create')
    ->with('Magento\Cms\Model\Block')
    ->willReturn($this->blockMock);

$this->modelBlockFactoryMock->expects($this->once())
    ->method('create')
    ->willReturn($this->blockMock);

この後、テストは合格です。

追加のクリーンアップ

次の点はあなたの質問とは何の関係もありませんが、とにかくそれらを書き留めておくことはできません。

この$objectManagerMockプロパティは廃止され、すべての参照をテストクラスから削除できます(実際には、削除する必要があります)。

次に、PHP 5.5以降では::class定数が利用可能です。これは、IDEでの自動リファクタリングと特定のクラスの使用法の検索に役立つため、クラス名に文字列を使用するよりもはるかに望ましい方法です。PHPStormをよりスマートにします。私は、例えば、一定、とのすべての文字列クラス名を代わるよう'Magento\Framework\App\RequestInterface'\Magento\Framework\App\RequestInterface::class

また、の使用について質問します\Magento\Framework\TestFramework\Unit\Helper\ObjectManager
私の意見では、を使用してテスト中のクラスを手動でインスタンス化する方が良いnewです。現時点でヘルパーが行う唯一のことは、\Magento\Framework\Registryモックを作成することです。私はそれを自分で作成し、コンストラクタの引数として指定したいと思います。これにより、テストコードを読み取るときにすべての依存関係が明確になります。

次のクリーンアップはかなり重要です。実装を正確に反映しないように単体テストの方法を変更します。
たとえば、リクエストモックのセットアップを以下に示しますtestDeleteActionThrowsException

$this->requestMock->expects($this->once())
    ->method('getParam')
    ->willReturn($this->blockId);

どれだけ頻繁getParamに呼び出されるかは本当に重要ですか?2回呼び出された場合、またはまったく呼び出されなかった場合、テストは失敗しますか?メソッドの最終結果をテストする限り、それは重要ではないと思います。
テストコードを実装により密接にバインドすることで、必要となる実装を厳格に維持することが難しくなります。
したがって、この例では、

$this->requestMock->expects($this->any())
    ->method('getParam')
    ->willReturn($this->blockId);

最後に、expects($this->any())がデフォルトであるため、混乱を減らすためにそれを削除することをお勧めします。

$this->requestMock->method('getParam')->willReturn($this->blockId);

これはもっといいです。

getParam元のテスト作成者が省略した場合でも、このテストで予期されるパラメーターを指定することはおそらく間違いありません。

$this->requestMock->method('getParam')
    ->with('block_id')
    ->willReturn($this->blockId);

これはおそらく私がテストを離れて次に進む方法です。

もう1つ考えてみgetParamましょう。ゲッターメソッドのような問題は、呼び出し元が異なる値にアクセスしようとすると、モックが引数の値に基づいて異なるものを返さなければならないことです。
将来的にこのような変更が行われる可能性が高いため、値が1つしかない場合でも、戻り値のマップを指定することがあります。これにより、テストされているクラスが将来変更された場合に、テストを維持しやすくなります。

$this->requestMock->method('getParam')
    ->willReturnMap([
        ['block_id', null, $this->blockId]
    ]);

PHPUnitの戻り値のマップに慣れていない場合nullは、配列の値はのオプションの2番目のパラメーターgetParam($key, $defaultValue = null)です。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.