You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Laravel/Codeception测试场景下如何对time()函数做stub打桩处理

测试实现方案

方案1:Laravel 内置时间伪造(推荐Laravel项目使用)

Laravel 框架自带的日期伪造能力不需要额外扩展,完全由官方维护,稳定性有保障:

  • 测试代码示例:
<?php

use Illuminate\Support\Facades\Date;
use Tests\TestCase;
use App\X;

class XTest extends TestCase
{
    public function testZ()
    {
        // 伪造当前时间为2024-01-01 00:00:00
        $fakeTime = Date::create(2024, 1, 1, 0, 0, 0);
        Date::travelTo($fakeTime);
        
        $x = new X();
        // 测试自定义参数
        $this->assertEquals(['test', $fakeTime->timestamp], $x->z('test'));
        // 测试默认参数
        $this->assertEquals(['c', $fakeTime->timestamp], $x->z());
        
        // 重置时间伪造,避免影响其他测试
        Date::travelBack();
    }
}

注:Laravel 9及以上版本的时间伪造能力可直接覆盖全局time()函数调用,不需要修改业务代码。

方案2:纯PHP命名空间函数覆盖(无框架依赖)

这是PHP语言原生支持的技巧,不需要任何扩展或第三方库:

适用条件

你的X类处于非全局的自定义命名空间下(例如namespace App\Services;)

实现原理

PHP解析全局函数调用时,会优先查找当前类所属命名空间下的同名函数,找不到才会调用全局函数。我们只需要在测试文件的同命名空间下自定义time()函数返回伪造值即可。

测试代码示例

假设X类的命名空间为App\Services:

<?php

// 和原类保持相同命名空间
namespace App\Services;

// 覆盖当前命名空间下的time()函数
function time()
{
    // 固定返回2024-01-01 00:00:00的时间戳
    return 1704067200;
}

use PHPUnit\Framework\TestCase;
use App\Services\X;

class XTest extends TestCase
{
    public function testZ()
    {
        $x = new X();
        $this->assertEquals(['test', 1704067200], $x->z('test'));
        $this->assertEquals(['c', 1704067200], $x->z());
    }
}

注:如果你的X类定义在全局命名空间下,该方案不适用,推荐使用方案3。

方案3:改造原类提升可测试性(兼容所有场景,长期最稳定)

如果可以修改原业务代码,推荐用依赖注入/方法抽离的方式改造,后续测试维护成本最低:

第一步:修改原X类

可以选择抽离为protected方法:

<?php

class X extends Y
{
    public function z(string $c = 'c') : array
    {
        return [$c, $this->getCurrentTimestamp()];
    }
    
    protected function getCurrentTimestamp(): int
    {
        return time();
    }
}

或者用依赖注入的方式实现(更符合SOLID原则):

<?php

class X extends Y
{
    protected $timeGenerator;
    
    public function __construct(callable $timeGenerator = null)
    {
        $this->timeGenerator = $timeGenerator ?? fn() => time();
    }

    public function z(string $c = 'c') : array
    {
        return [$c, ($this->timeGenerator)()];
    }
}

第二步:用Codeception Stub编写测试

针对抽离protected方法的版本:

<?php

public function testZ(Codeception\Test\Unit $t)
{
    $fakeTimestamp = 1704067200;
    // stub protected方法返回伪造时间
    $x = $t->make(X::class, [
        'getCurrentTimestamp' => $fakeTimestamp
    ]);
    
    $t->assertEquals(['test', $fakeTimestamp], $x->z('test'));
    $t->assertEquals(['c', $fakeTimestamp], $x->z());
}

针对依赖注入的版本,不需要使用mock能力,直接传入自定义时间生成函数即可:

<?php

public function testZ(Codeception\Test\Unit $t)
{
    $fakeTimestamp = 1704067200;
    $x = new X(fn() => $fakeTimestamp);
    
    $t->assertEquals(['test', $fakeTimestamp], $x->z('test'));
}

内容的提问来源于stack exchange,提问作者anon

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.30 14:24:04