Magento 2: Khắc phục cuộc gọi đến Phương thức không xác định Mock_BlockFactory_4b440480 :: tạo () Lỗi thử nghiệm


7

Gần đây tôi đã gửi một yêu cầu kéo tới Magento để sửa một phiên bản duy nhất của trình quản lý đối tượng đang được sử dụng trực tiếp.

Tuy nhiên, chạy thử nghiệm đơn vị hành trình của Magento không thành công với lỗi sau .

Lỗi nghiêm trọng của PHP: Gọi đến phương thức không xác định Mock_BlockFactory_4b440480 :: create () trong /home/travis/build/magento/magento2/app/code/Magento/Cms/Controll/Adminhtml/Block/Delete.php trên dòng 39

Dựa trên bản dựng travis, tôi thậm chí không thể biết thử nghiệm nào thất bại. Tôi đã có thể gặp lỗi tương tự (giống hệt nhau?) Tại địa phương với dấu vết ngăn xếp

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

Tôi đã có thể thu hẹp sự thất bại cục bộ trong bài kiểm tra này - nhưng tôi có một chút mất mát về những gì đang diễn ra.

Tôi đoán là khung kiểm tra đã tự động chế nhạo một đối số DI cho tôi, nhưng việc chế tạo tự động bị thiếu createphương thức. Nếu đó là trường hợp, thì câu hỏi thực tế của tôi là Làm thế nào để tôi thêm một bản giả cho một phụ thuộc mới được tiêm vào khung kiểm tra của Magento .

Tuy nhiên, tôi chưa bao giờ đi sâu xuống hố thỏ thử nghiệm của Magento, vì vậy tôi không chắc điều gì thực sự cần phải xảy ra ở đây. Bất cứ ai có kinh nghiệm thử nghiệm Magento có thể đặt thẳng cho tôi?


Tôi nghĩ bạn sẽ cần phải chế giễu nhà máy đối tượng được trả lại create, giống như ở đây .
phải

Câu trả lời:


11

Các \Magento\Framework\TestFramework\Unit\Helper\ObjectManagerkhông có khả năng tự động tạo ra một mô hình nhà máy.
(Một lưu ý phụ, tôi không bao giờ sử dụng \Magento\Framework\TestFramework\Unit\Helper\ObjectManager, vì tôi cố gắng giữ lượng ma thuật trong các bài kiểm tra đơn vị ở mức tối thiểu.)

Những thay đổi sau đây là bắt buộc để vượt qua bài kiểm tra:

Đầu tiên , tạo mock cho nhà máy khối mô hình trong thiết lập:

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

Hoặc, nhỏ gọn hơn:

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

Thứ hai , thêm giả lập mới vào các đối số hàm tạo của bộ điều khiển:

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

Thứ ba , cập nhật các thử nghiệm để họ không còn mong đợi Blockmô hình được tạo bởi người quản lý đối tượng giả mà bởi nhà máy mới:

Thay thế

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

với

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

Sau đó, bài kiểm tra vượt qua.

Dọn dẹp thêm

Những điểm sau đây không liên quan gì đến câu hỏi của bạn, nhưng dù sao tôi cũng không thể cưỡng lại việc viết chúng xuống.

Các $objectManagerMockbất động sản hiện nay đã lỗi thời và tất cả các tài liệu tham khảo để nó có thể (trên thực tế, nên ) được loại bỏ khỏi lớp thử nghiệm.

Tiếp theo, vì PHP 5.5 ::classkhông đổi có sẵn. Điều này được ưu tiên hơn nhiều so với việc sử dụng các chuỗi cho các tên lớp, vì nó hỗ trợ tái cấu trúc tự động trong IDE và tìm các cách sử dụng của một lớp nhất định. Nó làm cho PHPStorm thông minh hơn. Vì vậy, tôi sẽ thay thế tất cả các tên lớp chuỗi bằng hằng số, ví dụ như 'Magento\Framework\App\RequestInterface'với \Magento\Framework\App\RequestInterface::class.

Ngoài ra, tôi đặt câu hỏi về việc sử dụng \Magento\Framework\TestFramework\Unit\Helper\ObjectManager.
Theo tôi, tốt hơn là khởi tạo lớp đang được kiểm tra bằng tay new. Điều duy nhất người trợ giúp làm vào lúc này là nó tạo ra một bản \Magento\Framework\Registrygiả. Tôi muốn tự tạo nó và chỉ định nó như là một đối số của hàm tạo. Bằng cách đó, tất cả các phụ thuộc đều rõ ràng khi đọc mã kiểm tra.

Việc dọn dẹp tiếp theo là khá quan trọng. Tôi sẽ thay đổi các phương pháp kiểm tra đơn vị để không phản ánh chính xác việc thực hiện.
Ví dụ: thực hiện thiết lập giả định yêu cầu trong testDeleteActionThrowsException:

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

Có thực sự quan trọng như thế nào thường getParamđược gọi là? Thử nghiệm thất bại nếu nó được gọi hai lần, hoặc hoàn toàn không? Tôi nghĩ rằng điều đó không quan trọng, miễn là chúng tôi kiểm tra kết quả cuối cùng của phương pháp là những gì chúng tôi mong đợi.
Liên kết mã kiểm tra chặt chẽ hơn với việc thực hiện sau đó cần dẫn đến các kiểm tra cứng nhắc khó bảo trì hơn.
Vì vậy, ví dụ này tôi sẽ tái cấu trúc

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

Và cuối cùng, vì expects($this->any())là mặc định, nên loại bỏ điều đó để giảm lượng lộn xộn.

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

Cái này đọc đẹp hơn nhiều.

Có thể cho rằng việc chỉ định tham số dự kiến getParamtrong thử nghiệm này có thể có ý nghĩa , mặc dù tác giả thử nghiệm ban đầu đã bỏ qua nó.

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

Đây có lẽ là cách tôi sẽ rời khỏi bài kiểm tra và tiếp tục.

Mặc dù vậy, một suy nghĩ nữa: vấn đề với các phương thức getter getParamlà nếu người gọi cố gắng truy cập các giá trị khác nhau, thì giả phải trả về những thứ khác nhau dựa trên giá trị đối số.
Những thay đổi như vậy trong tương lai hoàn toàn có khả năng, vì vậy đôi khi tôi chỉ định bản đồ giá trị trả về, ngay cả khi chỉ có một giá trị. Điều này giúp dễ dàng duy trì kiểm tra khi lớp đang được kiểm tra thay đổi trong tương lai.

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

Trong trường hợp bạn không quen thuộc với ánh xạ giá trị trả về PHPUnit, nullgiá trị trong mảng là tham số thứ hai tùy chọn getParam($key, $defaultValue = null).

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.