วิธีที่เหมาะสมสำหรับการทดสอบหน่วยโค้ด PHP7 ด้วย PHPUnit 4.1 ใน Magento 2 คืออะไร


23

เมื่อฉันเขียนโมดูลของฉันฉันพยายามจัดหาการทดสอบหน่วยสำหรับส่วนที่สำคัญที่สุดของแอปพลิเคชัน อย่างไรก็ตามในขณะนี้ (Magento 2.1.3) มีหลายวิธีในการเขียนแบบทดสอบหน่วย:

วิธีทดสอบต่าง ๆ

  • รวมเข้ากับbin/magento dev:tests:run unitและรันบนการตั้งค่า phpunit เริ่มต้นที่มาพร้อมกับ Magento
  • เขียนแยกต่างหากเรียกใช้พวกเขาด้วยvendor/bin/phpunit app/code/Vendor/Module/Test/Unitและเยาะเย้ยทุกอย่างที่เป็นวีโอไอพี
  • เขียนแยกต่างหากจำลองทุกอย่างและใช้ 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 :-(

สถานการณ์ปัจจุบันของฉัน

เป็นเพราะเหตุนี้ตอนนี้ฉันส่วนใหญ่เขียนการทดสอบหน่วยของฉันในวิธีที่สาม (โดยการเรียกรุ่น 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();
}

ด้วยวิธีนี้เมื่อการทดสอบของฉันดำเนินการโดย PHPUnit ในตัวของ Magentos จะไม่เกิดข้อผิดพลาด

คำถามของฉัน

ดังนั้นนี่คือคำถามของฉัน: นี่คือวิธีการเขียนการทดสอบหน่วยที่ดีต่อสุขภาพหรือไม่ เพราะมันดูไม่ถูกต้องสำหรับฉันว่า Magento มาพร้อมกับเครื่องมือมากมายเพื่อช่วยในการทดสอบและฉันไม่สามารถใช้งานได้เพราะฉันใช้ PHP7 ฉันรู้ว่ามีตั๋วใน GitHub ที่แก้ไขปัญหานี้ แต่ฉันสงสัยว่าชุมชนกำลังเขียนบททดสอบอยู่หรือไม่

มีวิธีเขียนการทดสอบหน่วยใน Magento 2 หรือไม่ดังนั้นฉันไม่ต้อง 'ลดระดับ' รหัสของฉันและยังสามารถใช้ตัวช่วยในตัวของ Magentos สำหรับการเยาะเย้ยทุกอย่างที่ผู้จัดการวัตถุแตะต้อง? หรือมันเป็นวิธีปฏิบัติที่ไม่ดีที่จะใช้ตัวจัดการวัตถุแม้ในการทดสอบหน่วยของคุณ?

ฉันไม่มีคำแนะนำ / ตัวอย่างมากมายในวิธีที่เหมาะสมในการทดสอบหน่วยโมดูลที่คุณกำหนดเอง


1
เป็นคำถามที่ดีมาก
camdixon

คำตอบ:


17

การใช้เวอร์ชัน PHPUnit ที่รวมมาด้วยแม้ว่าจะเป็นรุ่นเก่าก็ตามอาจเป็นวิธีที่ดีที่สุดในการดำเนินการตั้งแต่นั้นจะช่วยให้สามารถทำการทดสอบสำหรับโมดูลทั้งหมดเข้าด้วยกันระหว่าง CI

ฉันคิดว่าการเขียนการทดสอบในลักษณะที่ไม่เข้ากันกับกรอบการทดสอบที่รวมมาจะช่วยลดค่าของการทดสอบได้อย่างมาก
แน่นอนคุณสามารถตั้งค่า CI เพื่อรันการทดสอบของคุณด้วย PHPUnit รุ่นต่าง ๆ แต่นั่นเป็นการเพิ่มความซับซ้อนให้กับระบบการสร้าง

ที่กล่าวว่าฉันเห็นด้วยกับคุณว่ามันไม่คุ้มค่าที่จะสนับสนุน PHP 5.6 ฉันใช้คำใบ้ประเภทเซนต์คิตส์และเนวิส PHP7 ประเภทผลตอบแทนมากที่สุดเท่าที่เป็นไปได้ (รวมทั้งฉันไม่สนใจเกี่ยวกับตลาด)

เพื่อหลีกเลี่ยงข้อ จำกัด ของไลบรารี่ PHPUnit 4.1 การเยาะเย้ยมีอย่างน้อยสองวิธีแก้ปัญหาที่ค่อนข้างตรงไปตรงมาที่ฉันเคยใช้ในอดีต:

  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 ไลบรารีที่เยาะเย้ยทั้งหมดที่ฉันพยายามสามารถนำไปใช้กับกรอบการทดสอบได้อย่างง่ายดายแม้แต่ PHPUnit เวอร์ชันเก่าเช่น 4.1

ไม่มีข้อได้เปรียบทางเทคนิคใด ๆ เหนือสิ่งอื่นใด คุณสามารถใช้การทดสอบตรรกะคู่ที่จำเป็นด้วยเช่นกัน

โดยส่วนตัวแล้วฉันชอบที่จะใช้คลาสที่ไม่ระบุชื่อเพราะนั่นไม่ได้เพิ่มจำนวนการพึ่งพาจากภายนอกและมันก็สนุกมากที่จะเขียนมันในแบบนั้น

แก้ไข :
เพื่อตอบคำถามของคุณ:

การเยาะเย้ย 'แก้ไข' ปัญหาของ PHPUnit 4.1.0 ไม่สามารถจัดการการแนะนำประเภท PHP7 ได้อย่างถูกต้องหรือไม่

ใช่ดูตัวอย่างด้านล่าง

และประโยชน์ของการเรียนแบบไม่ระบุชื่อมากกว่าการเยาะเย้ยคืออะไร?

การใช้คลาสที่ไม่ระบุชื่อเพื่อสร้างการทดสอบคู่ผสมนั้นก็เป็น "การเยาะเย้ย" ก็ไม่แตกต่างจากการใช้ห้องสมุดจำลองเช่น PHPUnits หรือการเยาะเย้ยหรืออย่างอื่น
การจำลองนั้นใช้การทดสอบสองประเภทโดยไม่คำนึงว่าจะสร้างอย่างไร
ข้อแตกต่างเล็ก ๆ น้อย ๆ ระหว่างการใช้คลาสที่ไม่ระบุชื่อหรือไลบรารีที่เยาะเย้ยคือคลาสที่ไม่ระบุชื่อไม่มีการพึ่งพาไลบรารีภายนอกเนื่องจากเป็นเพียง PHP ธรรมดา มิฉะนั้นจะไม่มีประโยชน์หรือข้อเสียเปรียบ มันเป็นเรื่องของการตั้งค่า ฉันชอบเพราะมันแสดงให้เห็นว่าการทดสอบไม่ได้เกี่ยวกับกรอบการทดสอบหรือการเยาะเย้ยห้องสมุดการทดสอบเป็นเพียงการเขียนโค้ดที่รันระบบภายใต้การทดสอบและตรวจสอบการทำงานโดยอัตโนมัติ

และวิธีการอัปเดตเวอร์ชัน PHPUnit ในไฟล์ main composer.json เป็น 5.3.5 (เวอร์ชันล่าสุดที่สนับสนุน PHP7 และมีวิธีการเยาะเย้ยสาธารณะ (จำเป็นต้องทดสอบด้วยตัวเองของ Magento 2)?

สิ่งนี้อาจมีปัญหาเนื่องจากการทดสอบในโมดูลอื่นและแกนทดสอบด้วย PHPUnit 4.1 เท่านั้นและคุณอาจพบข้อผิดพลาดที่ผิดพลาดใน CI ฉันคิดว่าเป็นการดีที่สุดที่จะใช้ PHPUnit เวอร์ชันที่รวมมาด้วยเหตุผลนั้น @ maksek กล่าวว่าพวกเขาจะอัปเดต PHPUnit แต่ไม่มี ETA สำหรับสิ่งนั้น


ตัวอย่างการทดสอบที่มีการทดสอบสองเท่าของคลาสที่ต้องการ PHP7 ที่รันด้วย PHPUnit 4.1 โดยใช้ไลบรารี่ Mockery:

<?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'));
    }
}

การเยาะเย้ย 'แก้ไข' ปัญหาของ PHPUnit 4.1.0 ไม่สามารถจัดการคำใบ้ประเภท PHP7 ได้อย่างถูกต้องหรือไม่ และประโยชน์ของการเรียนแบบไม่ระบุชื่อมากกว่าการเยาะเย้ยคืออะไร? และวิธีการอัปเดตเวอร์ชัน PHPUnit ในcomposer.jsonไฟล์หลักเป็น 5.3.5 (เวอร์ชันล่าสุดที่สนับสนุน PHP7 และมีวิธีการเยาะเย้ยสาธารณะ (จำเป็นต้องทดสอบด้วยตัวเองของ Magento 2)? คำถามอื่น ๆ อีกมากมายในตอนนี้ ...
Giel Berkers

อัปเดตคำตอบของฉันเพื่อตอบคำถามของคุณ @GielBerkers
Vinai

ขอบคุณสำหรับคำตอบที่ดีของคุณ ชัดเจนแล้วตอนนี้! ฉันคิดว่าฉันจะไปลองเยาะเย้ยแล้ว คลาสที่ไม่ระบุชื่อดูเหมือนว่าฉันจะต้องคิดค้นสิ่งใหม่ ๆ อีกมากมายที่ Mockery เสนอให้แล้ว ครั้งแรกที่ฉันต้องการที่จะเรียนรู้พื้นฐานของ PHPUnit และไปจากที่นั่น ฉันคิดว่าตอนนี้ถึงเวลาแล้ว
Giel Berkers

ที่ดี! เพลิดเพลินกับการสำรวจ Mockery ห้องสมุดที่ยอดเยี่ยม ในขณะที่คุณอยู่ที่นี่อาจจะลองดูที่ hamcrest ด้วยเช่นกันไลบรารีการยืนยัน - มันจะถูกติดตั้งด้วย Mockery โดยอัตโนมัติ
Vinai

3

Magento 2 รองรับเวอร์ชัน PHP ถัดไป:

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

หมายความว่ารหัสทั้งหมดที่เขียนโดยทีมวีโอไอพีทำงานได้กับทุกเวอร์ชั่นที่รองรับ

ดังนั้นทีมวีโอไอพีไม่ได้ใช้ฟีเจอร์ 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
Vinai
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.