phpunit หลีกเลี่ยงข้อโต้แย้งของตัวสร้างสำหรับการเยาะเย้ย


85

อะไรคือวิธีที่จะหลีกเลี่ยงไม่ให้ phpunit เรียกตัวสร้างสำหรับวัตถุจำลอง? มิฉะนั้นฉันจะต้องมีวัตถุจำลองเป็นอาร์กิวเมนต์ตัวสร้างอีกชิ้นหนึ่งสำหรับสิ่งนั้นเป็นต้น api ดูเหมือนจะเป็นดังนี้:

getMock($className, $methods = array(), array $arguments = array(),
        $mockClassName = '', $callOriginalConstructor = TRUE,
        $callOriginalClone = TRUE, $callAutoload = TRUE)

ฉันไม่ได้ทำงาน มันยังคงบ่นเกี่ยวกับอาร์กิวเมนต์ตัวสร้างแม้ว่าจะ$callOriginalConstructorตั้งค่าเป็นเท็จก็ตาม

ฉันมีวัตถุเพียงชิ้นเดียวในตัวสร้างและเป็นการฉีดแบบพึ่งพา ดังนั้นฉันไม่คิดว่าฉันมีปัญหาในการออกแบบที่นั่น

คำตอบ:


139

คุณสามารถใช้getMockBuilderแทนเพียงgetMock:

$mock = $this->getMockBuilder('class_name')
    ->disableOriginalConstructor()
    ->getMock();

ดูส่วนที่เกี่ยวกับ"การทดสอบคู่"ในเอกสาร PHPUnit ของรายละเอียด

แม้ว่าคุณจะทำได้ แต่จะดีกว่ามากถ้าไม่จำเป็นต้องทำ คุณสามารถ refactor โค้ดของคุณได้ดังนั้นแทนที่จะต้องฉีดคลาสคอนกรีต (ที่มีตัวสร้าง) คุณจะขึ้นอยู่กับอินเทอร์เฟซเท่านั้น ซึ่งหมายความว่าคุณสามารถจำลองหรือทำให้ส่วนต่อประสานได้โดยไม่ต้องแจ้งให้ PHPUnit แก้ไขพฤติกรรมตัวสร้าง


วิธีนี้ใช้ได้ผลดีสำหรับฉัน ควรเป็นตัวอย่าง 10.3 แม้ว่า ฉันพยายามแก้ไขโพสต์ แต่ดังนั้นกลับเตะมันกลับเพราะเป็นการแก้ไขที่สั้นเกินไป
Matthew

ขอบคุณ @Lytithwyn อัปเดตคำตอบ
dave1010

42

ได้แล้ว:

    // Get a Mock Soap Client object to work with.
    $classToMock = 'SoapClient';
    $methodsToMock = array('__getFunctions');
    $mockConstructorParams = array('fake wsdl url', array());
    $mockClassName = 'MyMockSoapClient';
    $callMockConstructor = false;
    $mockSoapClient = $this->getMock($classToMock,
                                     $methodsToMock,
                                     $mockConstructorParams,
                                     $mockClassName,
                                     $callMockConstructor);

ดูเหมือนว่าเกือบจะเป็นสิ่งที่ฉันต้องการ ฉันต้องการเรียก getMock ที่มีคลาสเท่านั้นที่จะล้อเลียนและ $ callMockConstructor อย่างไร? ประมาณนี้: $ this-> getMock ($ classToMock, $ callMockConstructor) สิ่งเดียวที่ฉันคิดได้คือไปที่ซอร์สของ PHPUnit และเปลี่ยนเป็น default = false
Gutzofter

1
ฉันเปลี่ยนค่าเริ่มต้นเป็นเท็จใน testcase.php คุณจะคิดว่ามันจะถูกตั้งค่าเป็นเท็จโดยค่าเริ่มต้น การล้อเลียนผู้สร้างดูเหมือนจะแปลกมาก
Gutzofter

คำตอบที่ยอดเยี่ยม แค่สิ่งที่ฉันกำลังมองหา
Hades

4

ในฐานะที่เป็นภาคผนวกฉันต้องการแนบการexpects()โทรไปยังวัตถุจำลองของฉันแล้วเรียกตัวสร้าง ใน PHPUnit 3.7.14 ออบเจ็กต์ที่ส่งคืนเมื่อคุณเรียกใช้disableOriginalConstructor()คืออ็อบเจกต์อย่างแท้จริง

// Use a trick to create a new object of a class
// without invoking its constructor.
$object = unserialize(
sprintf('O:%d:"%s":0:{}', strlen($className), $className)

น่าเสียดายที่ใน PHP 5.4 มีตัวเลือกใหม่ที่ไม่ได้ใช้:

ReflectionClass :: newInstanceWithoutConstructor

เนื่องจากไม่สามารถใช้งานได้ฉันจึงต้องสะท้อนคลาสด้วยตนเองแล้วจึงเรียกใช้ตัวสร้าง

$mock = $this->getMockBuilder('class_name')
    ->disableOriginalConstructor()
    ->getMock();

$mock->expect($this->once())
    ->method('functionCallFromConstructor')
    ->with($this->equalTo('someValue'));

$reflectedClass = new ReflectionClass('class_name');
$constructor = $reflectedClass->getConstructor();
$constructor->invoke($mock);

โปรดทราบว่าหากfunctionCallFromConstructเป็นprotectedเช่นนั้นคุณต้องใช้โดยเฉพาะเพื่อsetMethods()ให้มีการเยาะเย้ยวิธีการป้องกัน ตัวอย่าง:

    $mock->setMethods(array('functionCallFromConstructor'));

setMethods()ต้องโทรก่อนการexpect()โทร ส่วนตัวผมห่วงโซ่นี้หลังจากที่แต่ก่อนdisableOriginalConstructor()getMock()


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

1

บางทีคุณอาจต้องสร้างต้นขั้วเพื่อส่งผ่านเป็นอาร์กิวเมนต์ตัวสร้าง จากนั้นคุณสามารถทำลายโซ่ของวัตถุจำลองนั้นได้


1

หรือคุณสามารถเพิ่มพารามิเตอร์ให้กับgetMockเพื่อป้องกันการเรียกใช้ตัวสร้างเริ่มต้น

$mock = $this->getMock(class_name, methods = array(), args = array(), 
        mockClassName = '', callOriginalConstructor = FALSE);

ถึงกระนั้นฉันคิดว่าคำตอบของ dave1010 ดูดีกว่านี้เป็นเพียงเพื่อความสมบูรณ์เท่านั้น


1

คำถามนี้ค่อนข้างเก่า แต่สำหรับผู้เยี่ยมชมใหม่คุณสามารถทำได้โดยใช้createMockวิธีการ (ก่อนหน้านี้เรียกcreateTestDoubleและแนะนำใน v5.4.0)

$mock = $this->createMock($className);

ในขณะที่คุณสามารถมองเห็นในโค้ดด้านล่างสกัดจากที่PHPUnit\Framework\TestCaseระดับ (ในphpunit/src/framework/TestCase.php) นั้นโดยทั่วไปจะสร้างวัตถุจำลองโดยไม่มีการเรียกตัวสร้างเดิม

/** PHPUnit\Framework\TestCase::createMock method */
protected function createMock(string $originalClassName): MockObject
{
    return $this->getMockBuilder($originalClassName)
                ->disableOriginalConstructor()
                ->disableOriginalClone()
                ->disableArgumentCloning()
                ->disallowMockingUnknownTypes()
                ->getMock();
}

0

PHPUnit ถูกออกแบบมาเพื่อเรียกตัวสร้างบนวัตถุที่ถูกจำลอง เพื่อป้องกันสิ่งนี้คุณควร:

  1. ใส่วัตถุจำลองเป็นการอ้างอิงลงในวัตถุที่คุณมีปัญหาในการล้อเลียน
  2. สร้างคลาสทดสอบที่ขยายคลาสที่คุณพยายามเรียกที่ไม่เรียกคอนสตรัคเตอร์หลัก
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.