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
相关产品推荐
相关产品推荐

