2011-10-31 26 views
14

Tôi muốn có thể đặt thời gian cho mọi phiên bản của DateTime được khởi tạo trong khoảng thời gian của PHPUnit hoặc Kiểm tra Behat.Mocking Thời gian được sử dụng bởi tất cả các phiên bản của DateTime cho mục đích thử nghiệm.

Tôi đang thử nghiệm logic nghiệp vụ liên quan đến thời gian. Ví dụ, một phương thức trong một lớp chỉ trả về các sự kiện trong quá khứ hoặc tương lai.

của Thing Tôi không muốn làm nếu có thể:

1) Viết một wrapper xung quanh DateTime và sử dụng này thay vì DateTime khắp mã của tôi. Điều này sẽ liên quan đến một chút của một viết lại của cơ sở mã hiện tại của tôi.

2) Tự động tạo tập dữ liệu mỗi khi chạy thử nghiệm/bộ phần mềm.

Vì vậy, câu hỏi đặt ra là: Có thể ghi đè hành vi DateTimes để luôn cung cấp thời gian cụ thể khi được yêu cầu không?

+0

Bạn đã không chấp nhận một câu trả lời nào. Bạn có thể vui lòng làm rõ những gì bạn đang tìm kiếm trong một câu trả lời và tại sao các câu trả lời nhất định không đáp ứng bạn. – Gordon

+0

Đã có chính xác cùng một vấn đề, phần mở rộng timecop php từ câu trả lời của @shouze làm việc như một sự quyến rũ. –

Trả lời

15

Bạn nên đặt các phương pháp DateTime mà bạn cần trong các thử nghiệm của mình để trả về các giá trị mong đợi.

$stub = $this->getMock('DateTime'); 
    $stub->expects($this->any()) 
     ->method('theMethodYouNeedToReturnACertainValue') 
     ->will($this->returnValue('your certain value')); 

Xem http://www.phpunit.de/manual/3.6/en/test-doubles.html

Nếu bạn không thể còn sơ khai những phương pháp vì họ được mã hóa thành mã của bạn, có một cái nhìn tại

điều này giải thích làm thế nào để gọi một gọi lại bất cứ khi nào new được gọi. Sau đó bạn có thể thay thế lớp DateTime bằng một lớp DateTime tùy chỉnh có thời gian cố định. Một lựa chọn khác là sử dụng http://antecedent.github.io/patchwork

+0

Cảm ơn Gordon - Sự phụ thuộc vào DateTime được mã hóa cứng trong phần lớn mã của tôi. Tôi đã phạm sai lầm khi sử dụng nó như một nguyên thủy. Tất cả các phụ thuộc khác được tiêm nên dễ dàng giả lập. Tôi không muốn sử dụng một phần mở rộng để giả lập, vì điều này làm giảm tính di động của mã. Mặc dù nó có thể là lựa chọn duy nhất! Cảm ơn câu trả lời của bạn. –

2

Thêm vào những gì @Gordon đã chỉ ra có một, chứ không phải hackish, cách mã kiểm tra mà dựa vào thời gian hiện tại:

My mocking ra chỉ là một phương pháp bảo vệ mà được bạn là "toàn cầu" giá trị bạn có thể nhận được xung quanh các vấn đề cần phải tạo ra một lớp mình rằng bạn có thể yêu cầu những thứ như thời gian hiện tại (mà sẽ được sạch hơn nhưng trong php nó là khó hiểu/dễ hiểu mà mọi người không muốn làm cái đó).

Điều đó sẽ giống như thế này:

<?php 

class Calendar { 

    public function getCurrentTimeAsISO() { 
     return $this->currentTime()->format('Y-m-d H:i:s'); 
    } 

    protected function currentTime() { 
     return new DateTime(); 
    } 

} 
?> 

<?php 


class CalendarTest extends PHPUnit_Framework_TestCase { 

    public function testCurrentDate() { 
     $cal = $this->getMockBuilder('Calendar') 
      ->setMethods(array('currentTime')) 
      ->getMock(); 
     $cal->expects($this->once()) 
      ->method('currentTime') 
      ->will($this->returnValue(
       new DateTime('2011-01-01 12:00:00') 
      ) 
     ); 
     $this->assertSame(
      '2011-01-01 12:00:00', 
      $cal->getCurrentTimeAsISO() 
     ); 
    } 
} 
6

Bạn cũng có thể sử dụng thời gian du lịch lib trong đó sử dụng AOP gia hạn php PECL để mang lại những điều tương tự như ruby ​​khỉ vá https://github.com/rezzza/TimeTraveler

Ngoài ra còn có phần mở rộng php này , lấy cảm hứng từ chiếc đồng hồ thời gian ruby ​​một: https://github.com/hnw/php-timecop

+0

vui lòng thêm các giải pháp như nhận xét bên dưới câu hỏi .. – Lal

+0

Tôi muốn ... nhưng tôi không có đặc quyền để thực hiện ATM, đăng ký mới;) – shouze

+0

TimeTraveler lần cuối tôi thấy bị hỏng. Việc thay thế timecop là cách tốt hơn. –

0

Bạn có thể thay đổi triển khai của mình để tạo nhanh DateTime() một cách rõ ràng với time():

new \DateTime("@".time()); 

Điều này không làm thay đổi hành vi của lớp học.Nhưng bây giờ bạn có thể mock time() bằng cách cung cấp một chức năng namespaced:

namespace foo; 
function time() { 
    return 123; 
} 

Bạn cũng có thể sử dụng gói của tôi php-mock/php-mock-phpunit để làm như vậy:

namespace foo; 

use phpmock\phpunit\PHPMock; 

class DateTimeTest extends \PHPUnit_Framework_TestCase 
{ 

    use PHPMock; 

    public function testDateTime() 
    { 
     $time = $this->getFunctionMock(__NAMESPACE__, "time"); 
     $time->expects($this->once())->willReturn(123); 

     $dateTime = new \DateTime("@".time()); 
     $this->assertEquals(123, $dateTime->getTimestamp()); 
    } 
} 
0

Như tôi đang sử dụng Symfony's WebTestCase để thực hiện chức năng kiểm tra bằng cách sử dụng thử nghiệm PHPUnit bó, nó nhanh chóng trở thành không thực tế để giả lập tất cả các tập quán của lớp DateTime.

tôi muốn thử nghiệm các ứng dụng như nó xử lý các yêu cầu theo thời gian, chẳng hạn như bánh thử nghiệm hoặc hết hạn bộ nhớ cache vv

Cách tốt nhất mà tôi đã tìm thấy để làm điều này là để thực hiện lớp DateTime của riêng tôi mà kéo dài lớp mặc định và cung cấp một số phương thức tĩnh để cho phép một khoảng thời gian mặc định được thêm vào/trừ đối với tất cả các đối tượng DateTime được tạo từ điểm đó trở đi.

Đây là một tính năng thực sự dễ thực hiện và không yêu cầu cài đặt thư viện tùy chỉnh.

caveat emptor: Hạn chế duy nhất để phương pháp này là khuôn khổ Symfony (hoặc bất kỳ khuôn khổ bạn đang sử dụng) sẽ không sử dụng thư viện của bạn, vì vậy hành vi nào nó dự kiến ​​sẽ xử lý riêng của mình, chẳng hạn như nội cache/cookie sẽ hết hạn, sẽ không bị ảnh hưởng bởi những thay đổi này.

namespace My\AppBundle\Util; 

/** 
* Class DateTime 
* 
* Allows unit-testing of DateTime dependent functions 
*/ 
class DateTime extends \DateTime 
{ 
    /** @var \DateInterval|null */ 
    private static $defaultTimeOffset; 

    public function __construct($time = 'now', \DateTimeZone $timezone = null) 
    { 
     parent::__construct($time, $timezone); 
     if (self::$defaultTimeOffset && $this->isRelativeTime($time)) { 
      $this->modify(self::$defaultTimeOffset); 
     } 
    } 

    /** 
    * Determines whether to apply the default time offset 
    * 
    * @param string $time 
    * @return bool 
    */ 
    public function isRelativeTime($time) 
    { 
     if($time === 'now') { 
      //important, otherwise we get infinite recursion 
      return true; 
     } 
     $base = new \DateTime('2000-01-01T01:01:01+00:00'); 
     $base->modify($time); 
     $test = new \DateTime('2001-01-01T01:01:01+00:00'); 
     $test->modify($time); 

     return ($base->format('c') !== $test->format('c')); 
    } 

    /** 
    * Apply a time modification to all future calls to create a DateTime instance relative to the current time 
    * This method does not have any effect on existing DateTime objects already created. 
    * 
    * @param string $modify 
    */ 
    public static function setDefaultTimeOffset($modify) 
    { 
     self::$defaultTimeOffset = $modify ?: null; 
    } 

    /** 
    * @return int the unix timestamp, number of seconds since the Epoch (Jan 1st 1970, 00:00:00) 
    */ 
    public static function getUnixTime() 
    { 
     return (int)(new self)->format('U'); 
    } 

} 

Sử dụng này rất đơn giản:

public class myTestClass() { 
    public function testMockingDateTimeObject() 
    { 
     echo "fixed: ". (new DateTime('18th June 2016'))->format('c') . "\n"; 
     echo "before: ". (new DateTime('tomorrow'))->format('c') . "\n"; 
     echo "before: ". (new DateTime())->format('c') . "\n"; 

     DateTime::setDefaultTimeOffset('+25 hours'); 

     echo "fixed: ". (new DateTime('18th June 2016'))->format('c') . "\n"; 
     echo "after: ". (new DateTime('tomorrow'))->format('c') . "\n"; 
     echo "after: ". (new DateTime())->format('c') . "\n"; 

     // fixed: 2016-06-18T00:00:00+00:00 <-- stayed same 
     // before: 2016-09-20T00:00:00+00:00 
     // before: 2016-09-19T11:59:17+00:00 
     // fixed: 2016-06-18T00:00:00+00:00 <-- stayed same 
     // after: 2016-09-21T01:00:00+00:00 <-- added 25 hours 
     // after: 2016-09-20T12:59:17+00:00 <-- added 25 hours 
    } 
} 
Các vấn đề liên quan