ユニットテストでファクトリまたはプロキシを生成:「ReflectionException:Class…Factory does not exist」


8

私が理解している限りFactoryProxyクラスがまだ存在しない場合、クラスはオートローダーによってオンザフライで生成されますvar/generation(参照:Magento 2でのファクトリーの生成をトリガーするもの

しかし、ユニットテストで新しいファクトリを参照するときにこのエラーが発生するのはなぜですか?

ReflectionException:クラスMagento \ Framework \ Api \ Search \ SearchCriteriaBuilderFactoryは存在しません

[...] / vendor / magento / framework / TestFramework / Unit / Helper / ObjectManager.php:161

use Magento\Framework\Api\Search\SearchCriteriaBuilderFactory;
use Magento\Framework\TestFramework\Unit\Helper\ObjectManager;

class SearchCriteriaTest extends \PHPUnit_Framework_TestCase
{
    public function testFactoryGeneration()
    {
        $searchCriteriaBuilderFactory = (new ObjectManager($this))->getObject(SearchCriteriaBuilderFactory::class);
    }
}

ブートストラップファイルを使用していますdev/tests/unit/framework/bootstrap.php


クラスを生成するために見つけた回避策:

  • 実際のオブジェクトマネージャーを使用(ありがとう@DigitalPianism):

    \Magento\Framework\App\Bootstrap::create(BP, $_SERVER)->getObjectManager()->create('\Magento\Framework\Api\Search\SearchCrite‌​riaBuilderFactory')
  • 実行setup:di:compile(ファクトリーがコンストラクターで参照される場合)

しかし、私はまだクリーンでパフォーマンスの高いソリューションを見つけたいと思っています。

また、関連しているかどうかはわかりませんがcreate()、単体テストのオブジェクトマネージャーから生成されたファクトリーはを返しますnull。そのため、稼働中のファクトリーもまだありません。


確かに良い質問です。それは他のクラスでも起こりMagento\Framework\Api\Search\SearchCriteriaBuilderますか?
デジタルピアニズムのラファエル、

1
ランダムなコアクラス(APIインターフェイスではなく)を試して同じエラーが発生しました:ReflectionException:Class Magento \ Bundle \ Model \ Sales \ Order \ Pdf \ Items \ ShipmentFactory does not exist
Fabian Schmengler

あなたがしようとした場合はどうなります\Magento\Framework\App\Bootstrap::create(BP, $_SERVER)->getObjectManager()->create('\Magento\Framework\Api\Search\SearchCriteriaBuilderFactory');か?
デジタルピアニズムでのラファエル2016年

興味深いことに、それは機能しますが、実際のオブジェクトマネージャを単体テストでインスタンス化するのは適切ではないようです(このテストも10倍遅くなります)。別の方法があるといいのですが。
Fabian Schmengler、2016年

うん、悪い考え。あなたの代わりにgetObject電話したらどうなりますgetBuilderか?これはgetObject、テストのためだけに直接実行する必要があります。
デジタルピアニズムのラファエル、2016年

回答:


7

これに対処する最も簡単な方法は、テストを実行する前にコンパイルを実行することです。

bin/magento setup:di:compile

もう1つの方法は、ファクトリモックのメソッドを明示的に定義することです。これを行う代わりに:

$someFactoryMock = $this->getMockBuilder('Vendor\Module\Model\SomeFactory')
        ->disableOriginalConstructor()
        ->getMock();

これを行う:

$someFactoryMock = $this->getMockBuilder('Vendor\Module\Model\SomeFactory')
        ->disableOriginalConstructor()
        ->setMethods(['create'])
        ->getMock();

ある時点で、ObjectManager::getObjectモックを作成する前にを呼び出すことでこれに対処しようとしましたが、これはクリーンなソリューションとしては見えません。別のことはそれが役に立たなかったことです-それはオブジェクトを作成しましたが、var / generationにクラスを保存しませんでした。私はこれについてこれ以上掘り下げていません。


1
ファクトリーをモックすることが最もエレガントなソリューションであり、生成されなくても機能するため、私はこの回答を受け入れました。
Fabian Schmengler、2016年

5

問題は、必要なクラスを自動ロードできないため、PHPUnitモッキングライブラリに起因します。

Magento dev repoを確認すると、Autoloaderキャッチャーがセットアップされ、要求時にクラスが生成されます。モジュールリポジトリに同様のブートストラップファイルを作成すると、非常にうまく機能します。https//github.com/magento/magento2/blob/develop/dev/tests/unit/framework/autoload.php

<?php
/**
 * Copyright © 2016 Magento. All rights reserved.
 * See COPYING.txt for license details.
 */
$autoloader = new \Magento\Framework\TestFramework\Unit\Autoloader\ExtensionGeneratorAutoloader(
    new \Magento\Framework\Code\Generator\Io(
        new \Magento\Framework\Filesystem\Driver\File(),
        TESTS_TEMP_DIR . '/var/generation'
    )
);
spl_autoload_register([$autoloader, 'load']);

ただし、仮想ファイルシステムを利用して別のアプローチを使用することをお勧めします。これにより、生成されたクラスがインターフェイスのシグネチャの変更を変更しても、マテリアライズされた生成されたクラスはビルドを中断しません。

composer require --dev mikey179/vfsStream

そして、あなたのブートストラップファイルで:

$autoloader = new \Magento\Framework\TestFramework\Unit\Autoloader\ExtensionGeneratorAutoloader(
    new \Magento\Framework\Code\Generator\Io(
        new \Magento\Framework\Filesystem\Driver\File(),
        org\bovigo\vfs\vfsStream::setup('my_generated_classes')->url()
    )
);
spl_autoload_register([$autoloader, 'load']);

PHPSpec https://github.com/EcomDev/phpspec-magento-di-adapter/blob/master/src/Extension.php#L98のアダプターを作成するときに、私は同様のアプローチを使用していました


いいですね、試してみます
ファビアンシュメングラー2016年

4

また、あなたはこのようなものを使うかもしれません

private function getMockupFactory($instanceName)
{    
    /** Magento\Framework\TestFramework\Unit\Helper\ObjectManager */
    $objectManager = $this->objectManagerHelper;
    $factory = $this->getMockBuilder($instanceName . 'Factory')
        ->disableOriginalConstructor()
        ->setMethods(['create'])
        ->getMock();
    $factory->expects($this->any())
        ->method('create')
        ->will($this->returnCallback(function($args) use ($instanceName, $objectManager) {
            return $objectManager->getObject($instanceName, $args);
        }));
    return $factory;
}

そしてどこかコードのどこかで

class Some {
    __constructor(
        MyFactory $myFactory
      ){}
}

 $this->objectManagerHelper->getObject(Some::class,[
    'myFactory' => $this->getMockupFactory(My::class)
 ])

これのバリエーションを使用し、それは私のユースケースに完全に適合しました。そして、私はそれがジェネリックであるのが好きなので、今、私はファクトリーをモックする必要があるときはいつでも頼りになります。
tbernard

素晴らしい解決策。あなたが持つことができるので、それの行動を少し変更することができます$instanceNameし、$factoryNameあなたがデータモデルを返すことが期待されているインタフェースの工場を持っている場合。
Giel Berkers
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.