2011-10-31 9 views
14

मैं PHPUnit या Behat टेस्ट की अवधि के लिए तत्काल दिनांकटाइम के प्रत्येक इंस्टेंस के लिए समय निर्धारित करने में सक्षम होना चाहता हूं।परीक्षण उद्देश्यों के लिए दिनांक समय के सभी उदाहरणों द्वारा उपयोग किया जाने वाला समय मॉकिंग।

मैं समय से संबंधित व्यापार तर्क का परीक्षण कर रहा हूं। उदाहरण के लिए कि कक्षा में एक विधि केवल अतीत या भविष्य में घटनाओं को लौटाती है।

बात की मुझे क्या करना चाहते हैं यदि संभव हो तो न:

1) दिनांक समय चारों ओर एक आवरण लिखें और मेरे कोड भर दिनांक समय इस के बजाय का उपयोग करें। इसमें मेरे वर्तमान कोड बेस का पुनः लिखना शामिल होगा।

2) परीक्षण/सूट चलाने पर प्रत्येक बार गतिशील रूप से डेटासेट उत्पन्न करें।

तो सवाल यह है: क्या अनुरोध करते समय हमेशा एक विशिष्ट समय की आपूर्ति करने के लिए डेटटाइम व्यवहार को ओवरराइड करना संभव है?

+0

आपने अभी तक एक जवाब स्वीकार नहीं किया। क्या आप कृपया उत्तर में जो खोज रहे हैं उसे स्पष्ट कर सकते हैं और दिए गए उत्तरों आपको संतुष्ट क्यों नहीं करते हैं। – Gordon

+0

बिल्कुल वही समस्या थी, @Shouze के उत्तर से php timecop एक्सटेंशन एक आकर्षण की तरह काम करता था। –

उत्तर

15

आपको अपेक्षित मूल्यों को वापस करने के लिए अपने परीक्षणों में डेटटाइम विधियों को रोकना चाहिए।

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

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

आप तरीकों ठूंठ नहीं कर सकते, क्योंकि वे अपने कोड में hardcoded कर रहे हैं देखो,

जो कि कैसे एक आह्वान करने के लिए बताते हैं पर एक नजर है कॉलबैक जब भी new बुलाया जाता है। फिर आप डेटटाइम क्लास को एक कस्टम डेटटाइम क्लास के साथ प्रतिस्थापित कर सकते हैं जिसमें एक निश्चित समय हो।

मेरे बाहर मजाक सिर्फ एक संरक्षित विधि है कि हो जाता है: एक और विकल्प http://antecedent.github.io/patchwork

+0

धन्यवाद गॉर्डन - मेरे कोड के अधिकांश में डेटटाइम निर्भरता हार्डकोड की गई है। मैंने इसे आदिम के रूप में उपयोग करने की गलती की। अन्य सभी निर्भरताओं को इंजेक्शन दिया जाता है, इसलिए नकली करना आसान होता है। मैं नकली करने के लिए एक एक्सटेंशन का उपयोग नहीं करना चाहूंगा, क्योंकि इससे कोड की पोर्टेबिलिटी कम हो जाती है। हालांकि यह एकमात्र विकल्प हो सकता है! आपके उत्तर के लिए धन्यवाद। –

2

करने पर जोड़ा जा रहा है क्या @Gordon पहले ही बताया वहाँ एक है, बल्कि hackish, कि वर्तमान समय पर निर्भर करता है परीक्षण कोड का तरीका है उपयोग करने के लिए किया जाएगा आप "वैश्विक" मूल्य को कक्षा बनाने के लिए आवश्यक मुद्दों के आसपास हो सकते हैं, जिसे आप वर्तमान समय जैसी चीज़ों के लिए पूछ सकते हैं (जो क्लीनर होगा लेकिन PHP में यह तर्कसंगत/समझ में आता है कि लोग ऐसा नहीं करना चाहते हैं उस)।

कुछ इस तरह दिखेगा कि:

<?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

तुम भी समय यात्री lib जो इसी तरह की बातों को लाने के लिए aop php PECL विस्तार का उपयोग करता बंदर पैचिंग रूबी के लिए उपयोग कर सकते हैं https://github.com/rezzza/TimeTraveler

वहाँ भी इस php विस्तार है , रूबी टाइमकॉप से ​​प्रेरित एक: https://github.com/hnw/php-timecop

+0

कृपया प्रश्नों के नीचे टिप्पणियों के रूप में ऐसे समाधान जोड़ें .. – Lal

+0

मैं चाहूंगा ... लेकिन मुझे एटीएम, ताज़ा रजिस्टर करने का विशेषाधिकार नहीं है;) – shouze

+0

पिछली बार मैंने देखा था टाइमटाइवलर टूट गया था। टाइमकॉप विकल्प बेहतर तरीका है। –

0

आप DateTime() को तत्कालके साथ तुरंत कार्यान्वित करने के लिए अपना कार्यान्वयन बदल सकते हैं:

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

यह आपकी कक्षा के व्यवहार को नहीं बदलता है।लेकिन अब आप कर सकते हैं mock time() एक namespaced समारोह प्रदान करके:

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

तुम भी ऐसा करने के लिए अपने पैकेज php-mock/php-mock-phpunit इस्तेमाल कर सकते हैं:

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

जैसा कि मैंने Symfony's WebTestCase उपयोग कर रहा हूँ PHPUnit परीक्षण का उपयोग कर कार्यात्मक परीक्षण प्रदर्शन करने के लिए बंडल, डेटटाइम क्लास के सभी उपयोगों को नकल करने के लिए यह जल्दी ही अव्यवहारिक हो गया।

मैं के रूप में यह इस तरह के परीक्षण कुकी या कैश समाप्ति, आदि

सबसे अच्छा तरीका है मैं ऐसा करने के लिए मिल गया है मेरे अपने समयदिनाँक वर्ग फैली लागू करने के लिए है के रूप में समय के साथ अनुरोध, संभालती आवेदन का परीक्षण करना चाहता था डिफ़ॉल्ट वर्ग, और उस बिंदु से बनाई गई सभी डेटटाइम ऑब्जेक्ट्स को डिफॉल्ट टाइम स्कू को जोड़ने/घटाए जाने के लिए कुछ स्थैतिक विधियां प्रदान करने के लिए।

यह लागू करने के लिए वास्तव में एक आसान सुविधा है, और कस्टम पुस्तकालयों को स्थापित करने की आवश्यकता नहीं है।

चेतावनी एम्प्टर: इस विधि को केवल दोष यह है सिम्फोनी ढांचा (या जो भी ढांचा आप उपयोग कर रहे) अपने पुस्तकालय का उपयोग नहीं होगा, इसलिए किसी भी व्यवहार में यह इस तरह के आंतरिक कैश/कुकी के रूप में ही है, संभाल करने की उम्मीद है समाप्ति, इन परिवर्तनों से प्रभावित नहीं होंगे।

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'); 
    } 

} 

इस का उपयोग करते हुए सरल है:

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 
    } 
} 
संबंधित मुद्दे