วิธีการส่งออกใน CLI ในระหว่างการดำเนินการทดสอบหน่วย PHP?


151

เมื่อทำการทดสอบ PHPUnit ฉันต้องการถ่ายโอนข้อมูลออกเพื่อให้ฉันสามารถดีบักสิ่งหนึ่งหรือสอง

ฉันลองต่อไปนี้ (คล้ายกับตัวอย่างคู่มือ PHPUnit );

class theTest extends PHPUnit_Framework_TestCase
{
    /**
     * @outputBuffering disabled
     */
    public function testOutput() {
        print_r("Hello World");
        print "Ping";
        echo "Pong";
        $out = "Foo";
        var_dump($out);
    }   
}

ด้วยผลลัพธ์ต่อไปนี้:

PHPUnit @package_version@ by Sebastian Bergmann.

.

Time: 0 seconds, Memory: 3.00Mb

OK (1 test, 0 assertions)

สังเกตว่าไม่มีผลลัพธ์ที่คาดหวัง

ฉันใช้reposเวอร์ชันgitในวันที่ 19 กันยายน 2011

ผลลัพธ์ของphp -version:

$ php -version
PHP 5.2.9 (cli) (built: Dec  8 2010 11:36:37) 
Copyright (c) 1997-2009 The PHP Group
Zend Engine v2.2.0, Copyright (c) 1998-2009 Zend Technologies
    with Xdebug v2.1.0, Copyright (c) 2002-2010, by Derick Rethans

มีอะไรฉันทำผิดหรืออาจเป็นข้อผิดพลาด PHPUnit?


1
รหัสที่เรียกtestOutput()วิธีการอยู่ที่ไหน
Derrick Tucker

คุณลองอย่างหมดหวังจริงๆ (echo, print, print_r, var_dump - มันคือ "output") โดยทั่วไปฉันไม่มีปัญหาในการทำเอาต์พุตจากการทดสอบ คุณสามารถตรวจสอบว่าการบัฟเฟอร์เอาต์พุตถูกเปิดใช้งานหรือไม่: php.net/manual/en/function.ob-get-level.php - และวิธีที่ปลอดภัยที่สุดในการ "ทดสอบ" อย่างแข็งขันคือการยกเว้น BTW
hakre

3
@DerrickTucker PHPUnit ทำสิ่งนี้โดยการโทรphpunit /path/to/tests/theTest.php(ถ้าคลาสด้านบนอยู่ในไฟล์theTest.php)
Jess Telford

@hakre ผลตอบแทนob_get_level() 1แต่นี้ขัดแย้งกับรหัสต่อไปนี้: ซึ่งข้อผิดพลาดกับwhile (ob_get_level() > 0) { ob_end_flush(); } ob_end_clean(): failed to delete buffer. No buffer to delete.อยากรู้อยากเห็นและอยากรู้อยากเห็น
Jess Telford

1
มันบอกว่ามันเป็นรหัสของ phpunit ที่ก่อให้เกิดข้อผิดพลาด - เห็นได้ชัดเพราะ phpunits เอาท์พุทการกลืนใช้งานอยู่ (แต่คุณทำลายมัน) ดูอย่างแม่นยำชื่อฟังก์ชั่นก็แตกต่างกันเช่นกัน
hakre

คำตอบ:


196

UPDATE

เพิ่งรู้วิธีอื่นในการทำเช่นนี้ที่ทำงานได้ดีกว่า--verboseตัวเลือกบรรทัดคำสั่ง:

class TestSomething extends PHPUnit_Framework_TestCase {
    function testSomething() {
        $myDebugVar = array(1, 2, 3);
        fwrite(STDERR, print_r($myDebugVar, TRUE));
    }
}

สิ่งนี้ช่วยให้คุณสามารถถ่ายโอนข้อมูลใด ๆ ไปยังคอนโซลของคุณได้ตลอดเวลาโดยไม่มีเอาต์พุตที่ไม่ต้องการทั้งหมดที่มาพร้อมกับ--verboseตัวเลือก CLI


ดังที่คำตอบอื่น ๆ ได้ระบุไว้จะเป็นการดีที่สุดในการทดสอบผลลัพธ์โดยใช้วิธีการในตัวเช่น:

$this->expectOutputString('foo');

อย่างไรก็ตามบางครั้งการซนและดูการดีบักแบบครั้งเดียว / ชั่วคราวจะมีประโยชน์ในกรณีทดสอบของคุณ ไม่จำเป็นต้องมีการvar_dumpแฮ็ก / วิธีแก้ปัญหา สามารถทำได้โดยการตั้งค่า--verboseตัวเลือกบรรทัดคำสั่งเมื่อเรียกใช้ชุดทดสอบ ตัวอย่างเช่น:

$ phpunit --verbose -c phpunit.xml

สิ่งนี้จะแสดงผลลัพธ์จากภายในวิธีการทดสอบของคุณเมื่อทำงานในสภาพแวดล้อม CLI

ดู: การทดสอบการเขียน PHPUnit - ออกการทดสอบ


5
ขออภัยเราพลาดเขียนถึง stderr ใช้งานได้จริง ฉันเพียงแค่ถูกบังคับให้ใช้file_put_contents('php://stderr', $myDebugVar, FILE_APPEND);แทนเพราะผมมีข้อความUse of undefined constant STDERR - assumed 'STDERR'ที่มีfwrite
Serge

ปัญหาคือสิ่งนี้ดูเหมือนจะไม่ทำงานกับการแยกกระบวนการ
donquixote

@donquixote ไม่น่าแปลกใจที่การทดสอบจะดำเนินการในกระบวนการอื่นที่มีการส่งออกสตรีม STDERR ถูกยกเลิก ...
rdlowrey

1
คุณสามารถใช้STDOUTแทนได้STERR
Chris

2
ใช่. STDERRมันทำงานได้และดูเหมือนว่าจะส่งออกเช่นเดียวกับ ฉันใช้PHPUnit 4.5.0ในบรรทัด cmd ของ windows echoคำสั่งไม่ให้ผลเช่นเดียวกัน echoออก แต่เฉพาะหลังจากที่ผลการทดสอบปรากฏขึ้น fwrite(STDERR, 'string')หรือfwrite(STDOUT,'string')สร้างผลลัพธ์เดียวกัน: ผลลัพธ์ก่อนที่จะแสดงผลการทดสอบ
คริส

33

อัปเดต:ดูการอัปเดตของ rdlowrey ด้านล่างเกี่ยวกับการใช้งานfwrite(STDERR, print_r($myDebugVar, TRUE));ที่ง่ายขึ้น


ลักษณะการทำงานนี้คือความตั้งใจ (ตามjasonbarได้ชี้ให้เห็น ) สถานะความขัดแย้งของคู่มือถูกรายงานไปยัง PHPUnit

วิธีแก้ไขคือให้ PHPUnit ยืนยันว่าเอาต์พุตที่คาดว่าจะว่างเปล่า (เมื่อ infact มีเอาต์พุต) ซึ่งจะทริกเกอร์เอาต์พุตที่ไม่คาดคิดที่จะแสดง

class theTest extends PHPUnit_Framework_TestCase
{
    /**
     * @outputBuffering disabled
     */
    public function testOutput() {
        $this->expectOutputString(''); // tell PHPUnit to expect '' as output
        print_r("Hello World");
        print "Ping";
        echo "Pong";
        $out = "Foo";
        var_dump($out);
    }   
}

ให้:

PHPUnit @package_version@ by Sebastian Bergmann.

F

Time: 1 second, Memory: 3.50Mb

There was 1 failure:

1) theTest::testOutput
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-''
+'Hello WorldPingPongstring(4) "Foo"
+'

FAILURES!
Tests: 1, Assertions: 1, Failures: 1.

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


33

ลองใช้ดู --debug

มีประโยชน์หากคุณพยายามหาเส้นทางที่ถูกต้องไปยังไฟล์รวมหรือแหล่งข้อมูล


2
นี่คือคำตอบที่ถูกต้องสำหรับฉัน งบ fwrite ทั้งหมดที่เขียนในคำตอบก่อนหน้านี้ไม่ทำงานสำหรับฉัน
Kim Stacks

9

มันไม่ได้เป็นข้อผิดพลาด แต่มีเจตนามาก ทางออกที่ดีที่สุดของคุณคือการเขียนไปยังไฟล์บันทึกบางประเภทและปรับแต่งล็อกเพื่อดูผลลัพธ์

ถ้าคุณกำลังพยายามที่จะส่งออกทดสอบตรวจสอบนี้ออก

นอกจากนี้:

หมายเหตุ : โปรดทราบว่า PHPUnit กลืนเอาท์พุททั้งหมดที่ปล่อยออกมาในระหว่างการดำเนินการทดสอบ ในโหมดเข้มงวดการทดสอบที่ปล่อยเอาต์พุตจะล้มเหลว


1
ถ้ามันตั้งใจแล้วแน่นอนว่าคู่มือจะไม่ให้ตัวอย่างของมัน ? ยังไม่พยายามทดสอบผลลัพธ์เอง เพียงแค่ใช้มันเพื่อผลบางอย่างที่ทำให้ลูกตาทดสอบล้มเหลวเมื่อไม่ควรทำ
Jess Telford

ตามที่เขียน: ปกติแล้วฉันไม่มีปัญหาที่จะสะท้อนออกมาเมื่อการทดสอบทำงาน คุณอาจมีการกำหนดค่าบางอย่างที่ดึงดูดการป้อนข้อมูล
hakre

1
ถ้ามันไม่ได้เจตนาแล้วแน่นอนคู่มือจะไม่พูดว่ามันเป็น
jasonbar

1
ดังนั้นจึงดูเหมือนว่ามีข้อขัดแย้งในเอกสาร @hakre ดูเหมือนจะอยู่ภายใต้ความประทับใจแบบเดียวกับที่ฉันเคยเป็น (ซึ่งไม่ควรกลืน) - ส่วนใดของเอกสารที่ถูกต้อง?
Jess Telford

การทดสอบการสร้างเอาต์พุตจะล้มเหลวเฉพาะเมื่อ --disallow-test-output (หรือไฟล์ conf มี beStrictAboutOutputDuringTests = "true") - ตอนนี้เอกสารประกอบกล่าวว่า "การทดสอบที่ปล่อยเอาต์พุตตัวอย่างเช่นโดยการเรียกใช้การพิมพ์ในโค้ดทดสอบหรือรหัสทดสอบ จะถูกทำเครื่องหมายว่ามีความเสี่ยงเมื่อเปิดใช้การตรวจสอบนี้ " phpunit.readthedocs.io/en/8.4/risky-tests.html#risky-tests
ตัวชี้ NULL

7

ฉันโชคดีกับVisualPHPUnitและมันช่วยแสดงผลได้ดี

class TestHello extends PHPUnit_Framework_TestCase 
{
    public function test_Hello() 
    {
        print "hello world";
    }
}

TestHello ผลลัพธ์


อืมทำไมต้องโหวต? สิ่งนี้มีประโยชน์อย่างไรในฐานะทางเลือกอื่นในการถ่ายโอนข้อมูลการดีบักในการทดสอบ PHPUnit
Bob Stein

1
ฉันเดาว่านี่คือ downvote เพราะหากใครลองใช้งานคุณจะได้รับข้อผิดพลาดทางไวยากรณ์ อันยิ่งใหญ่
Jimbo

D'oh ฉันลืมฟังก์ชั่น ตอนนี้แก้ไขทดสอบตัดและวางแล้ว ขอบคุณ @Jimbo
Bob Stein

น่าเสียดายที่ไม่สามารถใช้งานร่วมกับ PHP 7 ได้ในขณะนี้: "VisualPHPUnit ไม่รองรับ PHP 7 ในขณะนี้เนื่องจากวิธีการใช้งาน phpunit php 7 จะได้รับการสนับสนุนในรุ่นใหญ่ครั้งต่อไป"
leo

6

คุณควรคิดถึงความตั้งใจจริง ๆ ของคุณ: หากคุณต้องการข้อมูลตอนนี้เมื่อทำการดีบั๊กเพื่อแก้ไขการทดสอบคุณจะต้องใช้มันในสัปดาห์หน้าอีกครั้งเมื่อการทดสอบแตก

ซึ่งหมายความว่าคุณจะต้องการข้อมูลเสมอเมื่อการทดสอบล้มเหลว - และการเพิ่ม a var_dumpเพื่อค้นหาสาเหตุเป็นการทำงานมากเกินไป ค่อนข้างใส่ข้อมูลลงในการยืนยันของคุณ

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


1
ฉันเห็นด้วย 100% กับทุกสิ่งที่คุณพูด ฉันใช้ PHPUnit เพื่อทำการทดสอบการรวมระบบซึ่งเรียก API XML ของ Google ในท้ายที่สุด การทดสอบหน่วยทั้งหมดที่ส่งผ่าน (ด้วยการเรียก API ถูกจำลอง) แต่การทดสอบขั้นสุดท้าย (ด้วยการเรียก API แบบสด) ล้มเหลว กลับกลายเป็นว่าเป็นความผิดพลาดของ Google APIแต่ในระหว่างนี้ฉันต้องการถ่ายโอนการตอบกลับ HTTP แบบดิบ
Jess Telford

2
ถ้าคุณต้องการตรวจแก้จุดบกพร่องรหัสของคุณเพื่อให้บรรลุสิ่งที่คุณได้ระบุไว้ที่นี่
David Meister

2
นี่คือเหตุผลที่ฉันไม่ชอบคำตอบที่สองคาดเดาสิ่งที่ผู้ใช้ต้องการทำ ฉันมาที่นี่เพราะฉันมีการทดสอบที่รอการล้างแคช ด้วย 5 วินาทีแคช ttls นั่นหมายความว่าการทดสอบของฉันดูเหมือนจะแฮงค์ประมาณ 16 วินาที ฉันแค่อยากจะแจ้งให้ผู้ใช้ทราบว่าไม่มีอะไรผิดปกติเราแค่รอให้แคชหมดเวลา หากผู้คนสามารถตอบคำถามได้คนที่มีกรณีการใช้งานแบบอื่นจะได้รับคำตอบด้วย
user151841

4

ใน laravel 5 คุณสามารถใช้ dump (), ถ่ายโอนเนื้อหาจากการตอบกลับล่าสุด

class ExampleTest extends TestCase{
    public function test1()
    {
        $this->post('/user', ['name' => 'Gema']);
        $this->dump();
    }
}

จะช่วยให้


4

เพียงแค่ใช้--verboseธงเมื่อรันPHPUnit

$ phpunit --verbose -c phpunit.xml 

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

ฉันหวังว่านี่จะช่วยได้.


3

สำหรับบางกรณีเราสามารถใช้บางอย่างเช่นนั้นเพื่อส่งออกบางสิ่งไปยังคอนโซล

class yourTests extends PHPUnit_Framework_TestCase
{
    /* Add Warnings */
    protected function addWarning($msg, Exception $previous = null)
    {
        $add_warning = $this->getTestResultObject();
        $msg = new PHPUnit_Framework_Warning($msg, 0, $previous);
        $add_warning->addWarning($this, $msg, time());
        $this->setTestResultObject($add_warning);
    }

    /* Add errors */
    protected function addError($msg, Exception $previous = null)
    {
        $add_error = $this->getTestResultObject();
        $msg = new PHPUnit_Framework_AssertionFailedError($msg, 0, $previous);
        $add_error->addError($this, $msg, time());
        $this->setTestResultObject($add_error);
    }

    /* Add failures */
    protected function addFailure($msg, Exception $previous = null)
    {
        $add_failure = $this->getTestResultObject();
        $msg = new PHPUnit_Framework_AssertionFailedError($msg, 0, $previous);
        $add_failure->addFailure($this, $msg, time());
        $this->setTestResultObject($add_failure);
    }

    public function test_messages()
    {
        $this->addWarning("Your warning message!");
        $this->addError("Your error message!");
        $this->addFailure("Your Failure message");
    }

    /* Or just mark test states! */
    public function test_testMarking()
    {
        $this->markTestIncomplete();
        $this->markTestSkipped();
    }
}

3

Hackish แต่ใช้งานได้: โยนข้อยกเว้นด้วยเอาต์พุต debug เป็นข้อความ

class theTest extends PHPUnit_Framework_TestCase
{
    public function testOutput() {
        throw new \Exception("hello");
    }   
}

อัตราผลตอบแทน:

...
There was 1 error:

1) theTest::testOutput
Exception: hello

2

นี้ถูกนำมาจากPHPUnit เอกสารเกี่ยวกับการแข่งขัน

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

เพียงแทนที่__METHOD__ในรหัสด้านล่างด้วยสิ่งที่คุณต้องการที่จะส่งออก

ตัวอย่าง 4.2: ตัวอย่างที่แสดงวิธีการเทมเพลตทั้งหมดที่มี

<?php
class TemplateMethodsTest extends PHPUnit_Framework_TestCase
{
    public static function setUpBeforeClass()
    {
        fwrite(STDOUT, __METHOD__ . "\n");
    }

    protected function setUp()
    {
        fwrite(STDOUT, __METHOD__ . "\n");
    }

    protected function assertPreConditions()
    {
        fwrite(STDOUT, __METHOD__ . "\n");
    }

    public function testOne()
    {
        fwrite(STDOUT, __METHOD__ . "\n");
        $this->assertTrue(TRUE);
    }

    public function testTwo()
    {
        fwrite(STDOUT, __METHOD__ . "\n");
        $this->assertTrue(FALSE);
    }

    protected function assertPostConditions()
    {
        fwrite(STDOUT, __METHOD__ . "\n");
    }

    protected function tearDown()
    {
        fwrite(STDOUT, __METHOD__ . "\n");
    }

    public static function tearDownAfterClass()
    {
        fwrite(STDOUT, __METHOD__ . "\n");
    }

    protected function onNotSuccessfulTest(Exception $e)
    {
        fwrite(STDOUT, __METHOD__ . "\n");
        throw $e;
    }
}
?>

1

ฉันส่งออก Testresults HTML โดยใช้ในกรณีนี้มันมีประโยชน์ในการล้างเนื้อหา:

var_dump($array);
ob_flush();

มีวิธี PHP ที่สอง

flush() 

ซึ่งฉันไม่ได้ลอง


1

PHPUnit ob_start()ซ่อนเอาท์พุทที่มี เราสามารถปิดการใช้งานชั่วคราว

    public function log($something = null)
    {
        ob_end_clean();
        var_dump($something);
        ob_start();
    }

0

ฉันต้องแก้ไขซอร์สโค้ดเพื่อให้โค้ดนี้ใช้งานได้ดังนั้นคุณต้องเพิ่ม URL สำหรับrepos ที่มีเครื่องหมายเป็นก้ามปูนี้เพื่อให้ได้ผล

class TestCase extends \PHPUnit_Framework_TestCase
{
    /**
     *  Save last response
     * @var Response|null A Response instance
     */
    static $lastResponse;
    /**
     *  Modify to save response
     *
     * @param  string $method
     * @param  string $uri
     * @param  array $parameters
     * @param  array $files
     * @param  array $server
     * @param  string $content
     * @param  bool $changeHistory
     * @return \Illuminate\Http\Response
     */
    final public function call(
        $method,
        $uri,
        $parameters = [],
        $files = [],
        $server = [],
        $content = null,
        $changeHistory = true
    ) {

        $response = parent::call($method, $uri, $parameters, $files, $server, $content, $changeHistory);
        static::$lastResponse = $this->client->getResponse();
        return $response;
    }


    /**
     * Modify message to add response text
     *
     * @param mixed $value
     * @param PHPUnit_Framework_Constraint $constraint
     * @param string $message
     * @since  Method available since Release 3.0.0
     */
    final public static function assertThat($value, PHPUnit_Framework_Constraint $constraint, $message = '')
    {
        $message .= PHP_EOL . static::$lastResponse . PHP_EOL;
        parent::assertThat($value, $constraint, $message);
    }
}

0

ต่อไปนี้เป็นวิธีการที่มีประโยชน์สำหรับการพิมพ์ข้อความดีบั๊กใน PHPUnit 4.x:

  • syslog(LOG_DEBUG, "Debug: Message 1!");

    ตัวอย่างการปฏิบัติเพิ่มเติม:

    syslog(LOG_DEBUG, sprintf("%s: Value: %s", __METHOD__, var_export($_GET, TRUE)));

    โทรsyslog()จะสร้างข้อความเข้าสู่ระบบ (ดู: man syslog.conf)

    หมายเหตุ: ระดับเป็นไปได้: LOG_DEBUG, LOG_INFO, LOG_NOTICE, LOG_WARNING, LOG_ERRฯลฯ

    บน macOS เพื่อสตรีมข้อความ syslog แบบเรียลไทม์ให้รัน:

    log stream --level debug --predicate 'processImagePath contains "php"'
  • fwrite(STDERR, "LOG: Message 2!\n");

    หมายเหตุ: STDERRคงไม่สามารถใช้ได้ถ้าอ่านสคริปต์ PHP จากstdin นี่คือวิธีแก้ปัญหา

    หมายเหตุ: STDERRคุณสามารถระบุชื่อไฟล์แทนได้

  • file_put_contents('php://stderr', "LOG: Message 3!\n", FILE_APPEND);

    หมายเหตุ: ใช้วิธีนี้หากคุณไม่มีSTDERRการกำหนดค่าคงที่

  • register_shutdown_function('file_put_contents', 'php://stderr', "LOG: Message 4!\n", FILE_APPEND);

    หมายเหตุ: ใช้วิธีนี้หากคุณต้องการพิมพ์บางอย่างให้สุดโดยไม่ส่งผลต่อการทดสอบ

การถ่ายโอนข้อมูลตัวแปรใช้เช่นvar_export()"Value: " . var_export($some_var, TRUE) . "\n"

หากต้องการพิมพ์ข้อความด้านบนเฉพาะในโหมด verbose หรือ debug โปรดดู: มีวิธีบอกว่า --debug หรือ --verbose ถูกส่งไปยัง PHPUnit หรือไม่?


แม้ว่าการทดสอบผลลัพธ์จะเป็นส่วนหนึ่งของการทดสอบด้วยตนเองให้ลองดูที่: หน้าเอกสารการทดสอบผลลัพธ์


-1

หากคุณใช้ Laravel คุณสามารถใช้ฟังก์ชั่นการบันทึกเช่น info () เพื่อเข้าสู่ไฟล์บันทึก Laravel ภายใต้ที่จัดเก็บ / บันทึก ดังนั้นมันจะไม่ปรากฏในเทอร์มินัลของคุณ แต่ในไฟล์บันทึก

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