Hướng dẫn phpunit mock function return value - giá trị trả về của hàm giả lập phpunit

Hướng dẫn phpunit mock function return value - giá trị trả về của hàm giả lập phpunit

Một trong những điều khó khăn nhất về thử nghiệm đơn vị là cô lập thành phần bạn muốn kiểm tra. Để làm điều này, bạn phải tạo một thế giới ảo cho thành phần của mình tồn tại bên trong - như ma trận với các xác nhận. Và làm thế nào để chúng ta tạo ra kỳ quan khoa học viễn tưởng này? Câu trả lời nằm ở các đối tượng giả phpunit (còn được gọi là cuống hoặc nhân đôi thử nghiệm). Trước khi tôi nhận được chi tiết, ở đây, một câu chuyện cảnh báo về thử nghiệm chống lại sự phụ thuộc thực sự:

Một nhà phát triển đã từng được giao nhiệm vụ tạo ra một hệ thống để quản lý các nhiệm vụ tiếp thị khác nhau cho chủ nhân của mình - một ngân hàng lớn. Hệ thống này được thiết kế để gửi các chiến dịch bưu chính, quảng cáo SMS, bản tin và những thứ tương tự. Một lập trình viên giỏi, anh ta đã bắt gặp lỗi thử nghiệm, và tất cả đều ở trên tàu để đưa ra bộ thử nghiệm đơn vị hệ thống. Vì vậy, ông đặt người quản lý sự kiện chính thông qua các bước đi của nó. Ông đã tạo ra một số hồ sơ dựa trên các nhóm khách hàng khác nhau mà bộ phận nhắm mục tiêu - học sinh, học sinh đói, công chức, chuyên nghiệp trẻ, v.v. Ông đã viết một bài kiểm tra đã chuyển chúng theo phương pháp chính của thành phần sự kiện

class MyCommand
{
    function execute(Context $context, $input = 0)
    {
        // do stuff
        try {
            $importantvalue = $context->perform($input);
            // and more stuff with $importantvalue
            return ($importantvalue + 5);
        } catch (\Exception $e) {
            return -1;
        }

    }
}
2 sau đó đã kiểm tra kết quả. Anh ấy đã chạy bài kiểm tra vào tối thứ Tư. Tất cả đã diễn ra tốt đẹp, dường như.

Đó là cho đến giữa ngày thứ Sáu, anh ấy đã biết về sự cấu hình sai của cỗ máy phát triển của mình. Thử nghiệm của anh đã tương tác với các hệ thống trực tiếp. Nó được gọi là một thành phần đã hợp nhất một thư tiếp thị với dữ liệu thử nghiệm, sau đó được in các chữ cái được cấp cho đơn đặt hàng để chúng được đẩy ra hệ thống bưu chính. Vào sáng thứ Sáu, hai ngàn khách hàng có giá trị nhất của ngân hàng đã mở một lá thư bắt đầu từ“Dear Rich Bastard”

Mọi thứ không phải là kịch tính như vậy - nhưng câu chuyện không minh họa một điểm quan trọng. Càng xa càng tốt, một bài kiểm tra đơn vị nên tập trung vào một thành phần duy nhất. Mã khác nên được giữ ở mức tối thiểu. Trong bài đăng này, tôi sẽ chạy qua một số cách mà chúng ta có thể tạo ra các đối tượng giả phpunit có thể được cấu hình để đánh lừa một thành phần để tin rằng chúng là thực tế.

Một lớp học để kiểm tra và người ngoài cuộc

Hãy bắt đầu với một lớp có tên

class MyCommand
{
    function execute(Context $context, $input = 0)
    {
        // do stuff
        try {
            $importantvalue = $context->perform($input);
            // and more stuff with $importantvalue
            return ($importantvalue + 5);
        } catch (\Exception $e) {
            return -1;
        }

    }
}
3:

class Context
{
    function perform($input)
    {
        // work with databases and services
        // and complicated stuff you don't want to run
        // in a test
        return $input;
    }
}

Như bạn có thể thấy,

class MyCommand
{
    function execute(Context $context, $input = 0)
    {
        // do stuff
        try {
            $importantvalue = $context->perform($input);
            // and more stuff with $importantvalue
            return ($importantvalue + 5);
        } catch (\Exception $e) {
            return -1;
        }

    }
}
3 làm rất ít. Mặc dù vậy, hãy tưởng tượng rằng đó là thành phần trực tiếp mà lập trình viên không may của chúng ta không nên gọi. Thành phần chúng tôi muốn kiểm tra sẽ sử dụng
class MyCommand
{
    function execute(Context $context, $input = 0)
    {
        // do stuff
        try {
            $importantvalue = $context->perform($input);
            // and more stuff with $importantvalue
            return ($importantvalue + 5);
        } catch (\Exception $e) {
            return -1;
        }

    }
}
3. Là người thử nghiệm, chúng tôi muốn kiểm tra sự tương tác đó, nhưng chúng tôi không muốn
class MyCommand
{
    function execute(Context $context, $input = 0)
    {
        // do stuff
        try {
            $importantvalue = $context->perform($input);
            // and more stuff with $importantvalue
            return ($importantvalue + 5);
        } catch (\Exception $e) {
            return -1;
        }

    }
}
3 được chạy. Đây là thành phần chúng tôi muốn kiểm tra.

class MyCommand
{
    function execute(Context $context, $input = 0)
    {
        // do stuff
        try {
            $importantvalue = $context->perform($input);
            // and more stuff with $importantvalue
            return ($importantvalue + 5);
        } catch (\Exception $e) {
            return -1;
        }

    }
}

class MyCommand
{
    function execute(Context $context, $input = 0)
    {
        // do stuff
        try {
            $importantvalue = $context->perform($input);
            // and more stuff with $importantvalue
            return ($importantvalue + 5);
        } catch (\Exception $e) {
            return -1;
        }

    }
}
7 có phương thức
class MyCommand
{
    function execute(Context $context, $input = 0)
    {
        // do stuff
        try {
            $importantvalue = $context->perform($input);
            // and more stuff with $importantvalue
            return ($importantvalue + 5);
        } catch (\Exception $e) {
            return -1;
        }

    }
}
8 được cung cấp một đối tượng
class MyCommand
{
    function execute(Context $context, $input = 0)
    {
        // do stuff
        try {
            $importantvalue = $context->perform($input);
            // and more stuff with $importantvalue
            return ($importantvalue + 5);
        } catch (\Exception $e) {
            return -1;
        }

    }
}
3. Nó gọi là phương pháp
class MyCommandTest extends \PHPUnit_Framework_TestCase
{
    function testMock()
    {
        // code goes here
    }
}
0 của nó. Sau đó, nó hoạt động trên giá trị trả về bằng cách thêm năm vào nó. Nếu đối tượng
class MyCommand
{
    function execute(Context $context, $input = 0)
    {
        // do stuff
        try {
            $importantvalue = $context->perform($input);
            // and more stuff with $importantvalue
            return ($importantvalue + 5);
        } catch (\Exception $e) {
            return -1;
        }

    }
}
3 ném một ngoại lệ, phương thức
class MyCommand
{
    function execute(Context $context, $input = 0)
    {
        // do stuff
        try {
            $importantvalue = $context->perform($input);
            // and more stuff with $importantvalue
            return ($importantvalue + 5);
        } catch (\Exception $e) {
            return -1;
        }

    }
}
8 phục hồi và trả về -1.

Boilerplate PHPUNIT

Dưới đây là lớp kiểm tra đơn vị mà tôi sẽ sử dụng cho ví dụ này:

class MyCommandTest extends \PHPUnit_Framework_TestCase
{
    function testMock()
    {
        // code goes here
    }
}

Phpunit sẽ chạy phương pháp TestMock cho tôi. Tôi sẽ kiểm tra xem trên dòng lệnh:

$ phpunit test/MyCommandTest.php 

PHPUnit 5.5.4 by Sebastian Bergmann and contributors.

.                                                                   1 / 1 (100%)

Time: 271 ms, Memory: 2.75MB

OK (1 test, 0 assertions)

Tạo các đối tượng giả phpunit trả về một giá trị duy nhất

Hãy nhớ rằng nó là lớp

class MyCommand
{
    function execute(Context $context, $input = 0)
    {
        // do stuff
        try {
            $importantvalue = $context->perform($input);
            // and more stuff with $importantvalue
            return ($importantvalue + 5);
        } catch (\Exception $e) {
            return -1;
        }

    }
}
3 mà chúng tôi muốn giả. Chúng ta có thể làm điều đó rất đơn giản với phpunit bằng cách gọi
class MyCommandTest extends \PHPUnit_Framework_TestCase
{
    function testMock()
    {
        // code goes here
    }
}
4 và chuyển nó một tên lớp. Hãy làm nó

$mockcontext = $this->createMock(Context::class);

Bây giờ tôi có một đối tượng

class MyCommand
{
    function execute(Context $context, $input = 0)
    {
        // do stuff
        try {
            $importantvalue = $context->perform($input);
            // and more stuff with $importantvalue
            return ($importantvalue + 5);
        } catch (\Exception $e) {
            return -1;
        }

    }
}
3 giả. Tôi vẫn cần phải định cấu hình nó để
class MyCommandTest extends \PHPUnit_Framework_TestCase
{
    function testMock()
    {
        // code goes here
    }
}
6 trả về một cái gì đó khi được gọi.

$mockcontext->method("perform")->willReturn(5);

Phương pháp

class MyCommandTest extends \PHPUnit_Framework_TestCase
{
    function testMock()
    {
        // code goes here
    }
}
7 trả về một loại dao quân đội Thụy Sĩ để quản lý các giá trị và hành vi trả lại (
class MyCommandTest extends \PHPUnit_Framework_TestCase
{
    function testMock()
    {
        // code goes here
    }
}
8 - mặc dù tôi đã giành chiến thắng quá sâu vào cỏ dại). Có lẽ đơn giản nhất trong số các công cụ này là
class MyCommandTest extends \PHPUnit_Framework_TestCase
{
    function testMock()
    {
        // code goes here
    }
}
9. Bằng cách gọi nó, tôi đảm bảo rằng bất cứ khi nào đối tượng
class MyCommand
{
    function execute(Context $context, $input = 0)
    {
        // do stuff
        try {
            $importantvalue = $context->perform($input);
            // and more stuff with $importantvalue
            return ($importantvalue + 5);
        } catch (\Exception $e) {
            return -1;
        }

    }
}
3 của tôi, ____ ____20 được gọi là nó sẽ trả về
$ phpunit test/MyCommandTest.php 

PHPUnit 5.5.4 by Sebastian Bergmann and contributors.

.                                                                   1 / 1 (100%)

Time: 271 ms, Memory: 2.75MB

OK (1 test, 0 assertions)
2.

Thời gian để xem nó có hoạt động không:

$mycommand = new MyCommand();
        $testval = $mycommand->execute($mockcontext);
        self::assertEquals(10, $testval);

Bởi vì

$ phpunit test/MyCommandTest.php 

PHPUnit 5.5.4 by Sebastian Bergmann and contributors.

.                                                                   1 / 1 (100%)

Time: 271 ms, Memory: 2.75MB

OK (1 test, 0 assertions)
3 thêm 5 vào giá trị được trả về bởi đối tượng
class MyCommand
{
    function execute(Context $context, $input = 0)
    {
        // do stuff
        try {
            $importantvalue = $context->perform($input);
            // and more stuff with $importantvalue
            return ($importantvalue + 5);
        } catch (\Exception $e) {
            return -1;
        }

    }
}
3 của nó, tôi hy vọng giá trị trả về sẽ là
$ phpunit test/MyCommandTest.php 

PHPUnit 5.5.4 by Sebastian Bergmann and contributors.

.                                                                   1 / 1 (100%)

Time: 271 ms, Memory: 2.75MB

OK (1 test, 0 assertions)
5 - vì vậy, những gì tôi cắm vào khẳng định của mình.

Trả về các giá trị khác nhau cho nhiều cuộc gọi

Ví dụ trước là tốt nếu tôi muốn cùng một giá trị được trả về mỗi khi

$ phpunit test/MyCommandTest.php 

PHPUnit 5.5.4 by Sebastian Bergmann and contributors.

.                                                                   1 / 1 (100%)

Time: 271 ms, Memory: 2.75MB

OK (1 test, 0 assertions)
6 được gọi. Nhưng nếu tôi muốn ba giá trị khác nhau cho ba cuộc gọi riêng biệt thì sao? Giản dị:

$mockcontext = $this->createMock(Context::class);
        $mockcontext->method("perform")->will(
            $this->onConsecutiveCalls(2, 4, 6)
        );

Một cấu trúc hơi khác nhau cho cấu hình. Phương thức

$ phpunit test/MyCommandTest.php 

PHPUnit 5.5.4 by Sebastian Bergmann and contributors.

.                                                                   1 / 1 (100%)

Time: 271 ms, Memory: 2.75MB

OK (1 test, 0 assertions)
7 chấp nhận một loạt các đối tượng khác nhau thiết lập các cấu hình khác nhau. Phương pháp
$ phpunit test/MyCommandTest.php 

PHPUnit 5.5.4 by Sebastian Bergmann and contributors.

.                                                                   1 / 1 (100%)

Time: 271 ms, Memory: 2.75MB

OK (1 test, 0 assertions)
8 trả về
$ phpunit test/MyCommandTest.php 

PHPUnit 5.5.4 by Sebastian Bergmann and contributors.

.                                                                   1 / 1 (100%)

Time: 271 ms, Memory: 2.75MB

OK (1 test, 0 assertions)
9 - và điều đó thực hiện những gì nó nói trên hộp thiếc - đảm bảo rằng các cuộc gọi liên tiếp mang lại các giá trị đã cho.

Đây là các bài kiểm tra của tôi:

$testval = $mycommand->execute($mockcontext);
        self::assertEquals(7, $testval);
        $testval = $mycommand->execute($mockcontext);
        self::assertEquals(9, $testval);
        $testval = $mycommand->execute($mockcontext);
        self::assertEquals(11, $testval);

Nếu

$ phpunit test/MyCommandTest.php 

PHPUnit 5.5.4 by Sebastian Bergmann and contributors.

.                                                                   1 / 1 (100%)

Time: 271 ms, Memory: 2.75MB

OK (1 test, 0 assertions)
6 trả về 2, 4 và 6, tôi sẽ mong đợi
$ phpunit test/MyCommandTest.php 

PHPUnit 5.5.4 by Sebastian Bergmann and contributors.

.                                                                   1 / 1 (100%)

Time: 271 ms, Memory: 2.75MB

OK (1 test, 0 assertions)
3 sẽ trả lại 7,9 và 11 - và đó là những gì tôi nhận được.

Tạo phương pháp đối tượng sơ khai, ném một ngoại lệ

Cũng như hoạt động thử nghiệm, chúng ta thường cần kiểm tra khả năng của các thành phần để xử lý các điều kiện lỗi. Tại đây, cách bạn có thể tạo một đối tượng sơ khai ném một ngoại lệ thay vì trả về một giá trị:

$mockcontext = $this->createMock(Context::class);
        $mockcontext->method("perform")->will(
            $this->throwException(new \Exception())
        );
        $testval = $mycommand->execute($mockcontext);
        self::assertEquals(-1, $testval);

Hãy nhớ rằng nếu

class MyCommand
{
    function execute(Context $context, $input = 0)
    {
        // do stuff
        try {
            $importantvalue = $context->perform($input);
            // and more stuff with $importantvalue
            return ($importantvalue + 5);
        } catch (\Exception $e) {
            return -1;
        }

    }
}
3 ném một ngoại lệ,
class MyCommand
{
    function execute(Context $context, $input = 0)
    {
        // do stuff
        try {
            $importantvalue = $context->perform($input);
            // and more stuff with $importantvalue
            return ($importantvalue + 5);
        } catch (\Exception $e) {
            return -1;
        }

    }
}
7 bắt được nó và trả về -1? Bài kiểm tra này xác nhận nó.

Ánh xạ đối số để trả về các giá trị trong các đối tượng giả phpunit

Nếu bạn cần làm cho các đối tượng giả PHPunit của bạn trả về một giá trị cụ thể dựa trên một đối số (hoặc một tập hợp các đối số) thì bạn có thể sử dụng

$mockcontext = $this->createMock(Context::class);
4 để thiết lập các điều kiện của mình.
$mockcontext = $this->createMock(Context::class);
4 chấp nhận một mảng mảng. Mỗi mảng bên trong nên xác định bắt đầu bằng đối số hoặc đối số dự kiến ​​và kết thúc với giá trị bạn muốn được trả về khi một cuộc gọi phù hợp được thực hiện.

class MyCommand
{
    function execute(Context $context, $input = 0)
    {
        // do stuff
        try {
            $importantvalue = $context->perform($input);
            // and more stuff with $importantvalue
            return ($importantvalue + 5);
        } catch (\Exception $e) {
            return -1;
        }

    }
}
0

Vì vậy, trong ví dụ này, đưa ra một đối số của

$ phpunit test/MyCommandTest.php 

PHPUnit 5.5.4 by Sebastian Bergmann and contributors.

.                                                                   1 / 1 (100%)

Time: 271 ms, Memory: 2.75MB

OK (1 test, 0 assertions)
2, giả
class MyCommand
{
    function execute(Context $context, $input = 0)
    {
        // do stuff
        try {
            $importantvalue = $context->perform($input);
            // and more stuff with $importantvalue
            return ($importantvalue + 5);
        } catch (\Exception $e) {
            return -1;
        }

    }
}
3 của tôi sẽ trả lại
$ phpunit test/MyCommandTest.php 

PHPUnit 5.5.4 by Sebastian Bergmann and contributors.

.                                                                   1 / 1 (100%)

Time: 271 ms, Memory: 2.75MB

OK (1 test, 0 assertions)
5. Lưu ý rằng thứ tự của các mảng không quan trọng. Ở đây, tôi gọi
$ phpunit test/MyCommandTest.php 

PHPUnit 5.5.4 by Sebastian Bergmann and contributors.

.                                                                   1 / 1 (100%)

Time: 271 ms, Memory: 2.75MB

OK (1 test, 0 assertions)
2 trước và sau đó
$mockcontext->method("perform")->willReturn(5);
0 mặc dù các bản đồ được xác định theo cách ngược lại.

Sử dụng các cuộc gọi lại để quản lý logic phức tạp hơn

Mặc dù PHPUNIT cung cấp rất nhiều phương thức thuận tiện để trả về các giá trị từ các đối tượng giả, đôi khi bạn sẽ cần phải đi theo con đường tùy chỉnh. Nếu bạn cần kiểm soát nhiều hơn, bạn có thể chuyển cuộc gọi lại cho

$mockcontext->method("perform")->willReturn(5);
1:

class MyCommand
{
    function execute(Context $context, $input = 0)
    {
        // do stuff
        try {
            $importantvalue = $context->perform($input);
            // and more stuff with $importantvalue
            return ($importantvalue + 5);
        } catch (\Exception $e) {
            return -1;
        }

    }
}
1

Điều này cho phép tôi làm cho phương pháp giả của tôi

class MyCommandTest extends \PHPUnit_Framework_TestCase
{
    function testMock()
    {
        // code goes here
    }
}
0 tinh vi hơn. Tôi có thể kiểm tra bất kỳ đối số nào được cung cấp và tạo ra giá trị trả lại của tôi cho phù hợp. Trong trường hợp này, nếu đối số là
$mockcontext->method("perform")->willReturn(5);
3, tôi ném một ngoại lệ, nếu không tôi sẽ trả lại
$mockcontext->method("perform")->willReturn(5);
4. Các thử nghiệm của tôi xác nhận rằng
$ phpunit test/MyCommandTest.php 

PHPUnit 5.5.4 by Sebastian Bergmann and contributors.

.                                                                   1 / 1 (100%)

Time: 271 ms, Memory: 2.75MB

OK (1 test, 0 assertions)
3 trả về
$mockcontext->method("perform")->willReturn(5);
6 khi gặp một ngoại lệ và thêm năm cho bất kỳ giá trị nào khác được trả về bởi
class MyCommandTest extends \PHPUnit_Framework_TestCase
{
    function testMock()
    {
        // code goes here
    }
}
0 (và trong ví dụ này, giá trị trả về luôn là
$mockcontext->method("perform")->willReturn(5);
4).

Tất nhiên, có nhiều hơn nữa để thử nghiệm với các đối tượng giả phpunit, nhưng vì tôi phải xem xét giá trị trở lại này mỗi tháng hoặc lâu hơn, tôi nghĩ rằng tôi đã để nó ở đây để sử dụng. Tôi sẽ trở lại chủ đề trong các bài viết sau.

Hình ảnh: _ mô hình của người Hồi giáo - William Warby - Giấy phép CC bằng 2.0_
“Models” – William Warby – license
CC BY 2.0_

Làm thế nào để chế giễu phương pháp trong phpunit?

PHPUNIT cung cấp các phương thức được sử dụng để tự động tạo các đối tượng sẽ thay thế đối tượng gốc trong thử nghiệm của chúng tôi. Các phương thức Createmock ($ type) và GetMockBuilder ($ type) được sử dụng để tạo đối tượng giả. Phương thức Createmock ngay lập tức trả về một đối tượng giả của loại được chỉ định.createMock($type) and getMockBuilder($type) methods are used to create mock object. The createMock method immediately returns a mock object of the specified type.

Stub trong phpunit là gì?

Sơ khai.Các cuống được sử dụng với các phương thức truy vấn như các phương thức - các phương thức trả về mọi thứ, nhưng điều đó không quan trọng nếu chúng thực sự được gọi.$ stub = $ this-> createmock (someclass :: class);$ stub-> Phương thức ('getSomment') -> willreturn ('foo');$ sut-> hành động ($ stub);

Phpunit được sử dụng để làm gì?

PHPUNIT là một khung thử nghiệm hướng đến lập trình viên cho PHP.Đây là một ví dụ của kiến trúc Xunit cho các khung kiểm tra đơn vị.a programmer-oriented testing framework for PHP. It is an instance of the xUnit architecture for unit testing frameworks.

Chế giễu thử nghiệm là gì?

Chế giễu là gì?Việc chế giễu là một quá trình được sử dụng trong thử nghiệm đơn vị khi đơn vị được kiểm tra có các phụ thuộc bên ngoài.Mục đích của việc chế giễu là cô lập và tập trung vào mã được kiểm tra và không vào hành vi hoặc trạng thái của các phụ thuộc bên ngoài.a process used in unit testing when the unit being tested has external dependencies. The purpose of mocking is to isolate and focus on the code being tested and not on the behavior or state of external dependencies.