PHPUnit:如何模拟今天的日期而不将其作为参数传递?

时间:2011-08-26 21:43:44

标签: unit-testing phpunit

我正在我班上测试一个做日期检查的方法。问题是该方法取决于今天的日期(每天都在变化),这使得这很难测试。我如何模仿今天的日期,以便我的测试明天仍然通过?

5 个答案:

答案 0 :(得分:8)

我对PHP一无所知,但在Java和C#中我会传递一些描述的“时钟” - 不是今天的日期本身,而是一个你可以询问当前日期的对象/时间。然后在单元测试中,您可以传入一个对象,该对象可以提供您想要的任何日期 - 包括一个硬编码到测试中的日期。

这也适用于PHP吗?

答案 1 :(得分:6)

如果您不想传递保留外部接口的日期,那么最好使用“seam”来提供日期:

class MyClass {
  public function toBeTested() {
    $theDate = $this->getDate();
    ...
  }

  protected function getDate() {
    return date();
  }
}

一般情况下,这个课程正常。 然后,在单元测试中,不是测试MyClass,而是使用覆盖getDate()函数的内部类扩展MyClass:

class MyTest extends phpunittestcase (sorry, writing this freeform, syntax is not exact!!) {

  static $testDate;

  public function testToBeTested() {
    //set the date to be used
    MyTest::testDate = '1/2/2000';
    $classUnderTest = new MyClassWithDate();
    $this->assertEquals('expected', $classUnderTest->toBeTested());

  }

  //just pass back the expected date
  class MyClassWithDate extends MyClass {
    protected function getDate() {
      return MyTest::testDate;
    }
  }
}

在此代码中,您将测试您的真实类的扩展,但您的扩展会覆盖seam函数(getDate()),并返回您要用于此特定测试的日期。

再次,抱歉,如果有一些令人震惊的语法错误,这是徒手写的。

答案 2 :(得分:4)

虽然Jon的回答是“正确的方法”,但另一个选择是使用runkit extension暂时将date()和/或time()函数替换为返回固定值的函数测试。

  1. 请务必在runkit.internal_override中设置php.ini,以便重命名内置函数。
  2. 使用runkit_function_rename重命名原始函数。
  3. 使用原始名称重命名模拟函数。
  4. 测试。
  5. 重新命名你的模拟。
  6. 重新命名原件。
  7. 这里有一些完全未经测试的代码可以帮助解决这个问题:

    function mock_function($original, $mock) {
        runkit_function_rename($original, $original . '_original');
        runkit_function_rename($mock, $original);
    }
    
    function unmock_function($original, $mock) {
        runkit_function_rename($original, $mock);
        runkit_function_rename($original . '_original', $original);
    }
    

    您应该在setUp()tearDown()方法中使用这些方法,以确保不会干扰后续的其他测试。

答案 3 :(得分:3)

我知道你不想把它作为一个参数传递。但也许你可以重新考虑一下......

当从外部作为参数传递时,日期不是一个无关紧要的技术细节,而是一个重要的功能规则。你不需要以下任何一种吗?

  • 虽然当前日期可以是常规用例,但规则可能适用于其他日期。然后你的代码将更加通用,并在稍后的用例中工作,不做任何修改。这经常发生在我身上......
  • 多个代码可以在算法中使用当前日期。因为计算机的速度不是无限的,所以几个人会得到不同的瞬间......这在逻辑上是否合乎逻辑?或者使用相同的瞬间(例如,用户按下“火”按钮的那一刻)更准确?想想您以后如何在数据库中请求这些时间,如果它们在您的数据库中都是不同的,即使它们代表用户的同一时刻

答案 4 :(得分:0)

大卫说,我无法重命名两次,所以我得到了它:

function mockDate()
{
    runkit_function_rename('date', 'test_date_override');
    runkit_function_add('date','$format=NULL,$timestamp=NULL,$locale=NULL', 'return DATEMOCK;');
}

function unmockDate()
{
    runkit_function_remove('date');
    runkit_function_rename('test_date_override', 'date');
}