2009-08-07 28 views
10

Tôi đang kiểm tra đơn vị một số mã PHP với SimpleTest và tôi đã gặp sự cố. Trong các bài kiểm tra của tôi về một lớp cơ sở dữ liệu tôi muốn có thể thiết lập một kỳ vọng cho các hàm PHP mysql. Trong các thử nghiệm của tôi về một lớp bao bọc cho hàm mail tôi muốn giả lập hàm PHP mail. Đây chỉ là một vài ví dụ.Mocking các hàm PHP trong các bài kiểm tra đơn vị

Vấn đề là: Tôi không (luôn luôn) muốn kiểm tra xem lớp thư của tôi có gửi e-mail không, tôi muốn kiểm tra cách nó gọi hàm mail. Tôi muốn có thể kiểm soát những gì các chức năng này trở lại. Tôi muốn có thể kiểm tra lớp Cơ sở dữ liệu của mình mà không cần một cơ sở dữ liệu, đồ đạc và toàn bộ đó.

Tôi đã có một số kinh nghiệm về thử nghiệm mã Ruby và Test :: Unit và RSpec giúp bạn dễ dàng kiểm tra mã độc lập. Tôi là người mới để thử nghiệm PHP và cảm thấy như tôi đang thử nghiệm nhiều hơn tôi cần phải làm, để có được các bài kiểm tra của tôi để vượt qua.

Có cách nào trong SimpleTest hoặc PhpUnit hoặc một số khung kiểm tra khác làm cho điều này có thể hoặc dễ dàng hơn không?

Trả lời

10

Không theo cách tự động. Những gì bạn có thể làm, là viết mã của bạn theo cách sao cho các phụ thuộc bên ngoài được bao bọc trong các đối tượng được truyền vào từ bên ngoài. Trong môi trường sản xuất của bạn, bạn sẽ chỉ kết nối các bộ điều hợp thực, nhưng trong quá trình thử nghiệm, bạn có thể kết nối nó với các sơ đồ hoặc mock.

Nếu bạn thực sự nhấn mạnh, bạn có thể sử dụng runkit extension thay đổi mô hình lập trình của php để bạn có thể xác định lại các lớp và chức năng khi chạy. Đây là một phần mở rộng bên ngoài và không chuẩn, tuy nhiên, hãy nhớ rằng trong tâm trí. Tiêu chuẩn defacto là phương pháp thủ công như tôi đã mô tả ở trên.

+0

Tôi hiểu. Tôi mong đợi (nhưng không hy vọng) một câu trả lời như vậy. Tôi không thực sự nhấn mạnh, vì tôi muốn đơn giản hóa và tăng tốc độ kiểm tra, không làm cho nó phức tạp hơn. Cám ơn phản hồi của bạn! – avdgaag

+0

Hiện tại [Mockery] (http://docs.mockery.io/en/latest/cookbook/mocking_hard_dependencies.html) có vẻ là [cách đi] (http://stackoverflow.com/a/42158443/659788). – Franco

+1

@Franco Không dành cho nội trang. – troelskn

0

Có một phần mở rộng PHPUnit sử dụng runkit trong nội bộ, và có khả năng sử dụng các trình so khớp gọi, ràng buộc tham số và mọi thứ mà một đối tượng giả lập có thể thực hiện.

https://github.com/tcz/phpunit-mockfunction

0

Trong PHP 5.3+ môi trường bạn có thể workaround nhu cầu sử dụng runkit mở rộng bằng cách hack các không gian tên. Yêu cầu duy nhất trong đó các cuộc gọi hàm không sử dụng không gian tên đủ điều kiện như \mysql_query() - và chúng thường không. Sau đó, bạn có thể định nghĩa cùng một hàm trong thử nghiệm của mình, bằng cách xác định thử nghiệm trong một không gian tên, và PHP sẽ gọi hàm của bạn thay vì hàm toàn cầu. Cá nhân tôi sử dụng phương pháp này để mở cuộc gọi hàm time(). Đây là nice example with the mockery framework

+0

Bạn có thể sử dụng [php-mock] (https: // github.com/php-mock/php-mock) để tạo các mocks như vậy. –

1

Here is an interesting article viết về chế nhạo các chức năng php toàn cục. Tác giả đề xuất một giải pháp rất sáng tạo để 'Mock' (loại bỏ) các chức năng php toàn cầu bằng cách ghi đè lên các phương thức bên trong không gian tên của SUT (dịch vụ được thử nghiệm).

Đây mã từ một ví dụ trong bài viết trên blog nơi time hàm được chế giễu:

<?php 

namespace My\Namespace; 

use PHPUnit_Framework_TestCase; 

/** 
* Override time() in current namespace for testing 
* 
* @return int 
*/ 
function time() 
{ 
    return SomeClassTest::$now ?: \time(); 
} 

class SomeClassTest extends PHPUnit_Framework_TestCase 
{ 
    /** 
    * @var int $now Timestamp that will be returned by time() 
    */ 
    public static $now; 

    /** 
    * @var SomeClass $someClass Test subject 
    */ 
    private $someClass; 

    /** 
    * Create test subject before test 
    */ 
    protected function setUp() 
    { 
     parent::setUp(); 
     $this->someClass = new SomeClass; 
    } 

    /** 
    * Reset custom time after test 
    */ 
    protected function tearDown() 
    { 
     self::$now = null; 
    } 

    /* 
    * Test cases 
    */ 
    public function testOneHourAgoFromNoon() 
    { 
     self::$now = strtotime('12:00'); 
     $this->assertEquals('11:00', $this->someClass->oneHourAgo()); 
    } 
    public function testOneHourAgoFromMidnight() 
    { 
     self::$now = strtotime('0:00'); 
     $this->assertEquals('23:00', $this->someClass->oneHourAgo()); 
    } 
} 

Không chắc nếu điều này là một cách tốt để làm điều đó nhưng nó chắc chắn hoạt động tốt và tôi nghĩ rằng đó là đáng nói đây. Có thể là một số thực phẩm để thảo luận ...

Các vấn đề liên quan