Tạo nhà máy hoặc proxy trong các bài kiểm tra đơn vị: Phản xạ ngoại lệ: Nhà máy Class Class không tồn tại


8

Theo như tôi hiểu, FactoryProxycác lớp được tạo ra bởi trình tải tự động nếu chúng chưa tồn tại var/generation(xem: Điều gì kích hoạt thế hệ của một nhà máy trong Magento 2 )

Nhưng tại sao tôi lại gặp lỗi này khi tham khảo một nhà máy mới trong một bài kiểm tra đơn vị?

ReflectionException: Class Magento \ Framework \ Api \ Search \ SearchCriteriaBuilderFactory không tồn tại

[...] / nhà cung cấp / 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);
    }
}

Tôi đang sử dụng tập tin bootstrap dev/tests/unit/framework/bootstrap.php.


Cách giải quyết tôi tìm thấy để tạo lớp:

  • sử dụng trình quản lý đối tượng thực (Cảm ơn @DigitalPianism):

    \Magento\Framework\App\Bootstrap::create(BP, $_SERVER)->getObjectManager()->create('\Magento\Framework\Api\Search\SearchCrite‌​riaBuilderFactory')
  • chạy setup:di:compile(cho nhà máy được tham chiếu trong một hàm tạo)

Nhưng tôi vẫn hy vọng tìm được một giải pháp sạch sẽ và hiệu quả.

Ngoài ra, không chắc có liên quan hay không, nhưng create()của nhà máy được tạo từ trình quản lý đối tượng thử nghiệm đơn vị trả về null, vì vậy tôi thậm chí còn chưa có nhà máy hoạt động.


Câu hỏi hay thật đấy. Điều đó xảy ra với các lớp khác hay chỉ với Magento\Framework\Api\Search\SearchCriteriaBuilder?
Raphael tại Digital Pianism

1
Tôi đã thử một lớp lõi ngẫu nhiên (không phải giao diện api) và nhận được cùng một lỗi: ReflectionException: Class Magento \ Bundle \ Model \ Sales \ Order \ Pdf \ Item \ ShipmentFactory không tồn tại
Fabian Schmengler

Nếu bạn cố gắng \Magento\Framework\App\Bootstrap::create(BP, $_SERVER)->getObjectManager()->create('\Magento\Framework\Api\Search\SearchCriteriaBuilderFactory');thì sao?
Raphael tại Nghệ thuật piano kỹ thuật số

Thật thú vị, điều đó có hiệu quả nhưng dường như tôi không thể khởi tạo trình quản lý đối tượng thực trong các bài kiểm tra đơn vị (cũng làm cho bài kiểm tra này chậm hơn 10 lần) - tôi hy vọng có một cách khác.
Fabian Schmengler

Đúng, ý xấu. Nếu thay vì getObjectbạn gọi getBuilderthì sao? Điều đó sẽ xảy ra trực tiếp thông qua getObjectnhưng chỉ để kiểm tra.
Raphael tại Nghệ thuật piano kỹ thuật số

Câu trả lời:


7

Cách dễ nhất để giải quyết vấn đề đó là chạy biên dịch trước khi chạy thử nghiệm:

bin/magento setup:di:compile

Một cách khác là xác định rõ ràng các phương thức cho giả lập nhà máy, vd. thay vì làm điều này:

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

Làm cái này:

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

Tại một số điểm, tôi đã cố gắng giải quyết điều đó bằng cách gọi ObjectManager::getObjecttrước khi tạo giả, nhưng đây không phải là một giải pháp sạch. Một điều nữa là nó không giúp được gì - nó đã tạo ra một đối tượng, nhưng không lưu lớp trong var / thế hệ. Tôi không đào sâu vào vấn đề này nữa.


1
Tôi chấp nhận câu trả lời này bởi vì chế nhạo nhà máy là giải pháp tao nhã nhất và hoạt động ngay cả khi nó không được tạo ra.
Fabian Schmengler

5

Vấn đề bắt nguồn từ thư viện giả lập PHPUnit, vì nó không thể tự động tải lớp cần thiết.

Nếu bạn xem qua Magento dev repo, nó sẽ thiết lập trình bắt Autoloader, tạo ra một lớp khi được yêu cầu. Nếu bạn tạo tệp bootstrap tương tự trong kho mô-đun của mình, nó sẽ hoạt động khá tốt: 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']);

Tuy nhiên, tôi sẽ khuyên bạn nên sử dụng một cách tiếp cận khác, bằng cách sử dụng một hệ thống tệp ảo, vì vậy các lớp được tạo ra của bạn sẽ không phá vỡ bản dựng của bạn nếu thay đổi chữ ký giao diện lớp được tạo.

composer require --dev mikey179/vfsStream

Và sau đó trong tệp bootstrap của bạn:

$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']);

Tôi đã sử dụng cách tiếp cận tương tự khi tạo bộ điều hợp cho PHPSpec https://github.com/EcomDev/phpspec-magento-di-ad CHƯƠNG / blob / master / src /Extension.php # L98


Âm thanh tuyệt vời, tôi sẽ dùng thử
Fabian Schmengler

4

Ngoài ra, bạn có thể sử dụng một cái gì đó như thế này

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;
}

và một số nơi trong mã chỉ cần vượt qua

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

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

Đã sử dụng một biến thể của điều này và nó hoàn toàn phù hợp với trường hợp sử dụng của tôi. Và tôi thích nó chung chung vì vậy bây giờ tôi có bất cứ lúc nào tôi cần để chế giễu một nhà máy.
tbernard

Giải pháp tốt đẹp. Bạn có thể muốn thay đổi hành vi của mình một chút để bạn có thể có $instanceName$factoryNametrong trường hợp bạn có một nhà máy giao diện dự kiến ​​sẽ trả về các mô hình dữ liệu.
Giel Berkers
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.