请严格遵守以下单元测试的规则:
- 单元测试为单个函数测试,不是集成测试,所以如果当前被测函数依赖其他服务,或者依赖Model层,则需要对其使用替身,以避免其他服务对当前测试的影响。
- 单元测试要尽量覆盖所有逻辑分支,包括正常分支和异常分支。
- 给每个单元测试增加注释,说明测试的场景和预期结果。
首先增加model替身,例如:mockAModel和mockBModel
<?php
use App\Model\AModel;
use App\Model\BModel;
protected function setUp(): void
{
parent::setUp();
$this->mockAModel = Mockery::mock('overload:' . AModel::class);
$this->mockBModel = Mockery::mock('overload:' . BModel::class);
}
如果我们在 Service 代码中存在如下使用 Model 的代码:
AModel::query()
->whereBetween('log_date', [$yesterday, $today])
->where('locale', $this->localeName)
->select('product_id', 'product_price', 'log_date')
->get();
则我们需要在 Test 中如下对Model链式操作进行Mock
$mockAModelList = Collection::make([
[
'product_id' => 1,
'log_date' => '2024-01-01',
'locale' => 'us',
'product_price' => 100,
]
]);
$this->mockAModel
->shouldReceive('query->whereBetween->where->select->get')
->andReturn($mockAModelList);
如果我们当前Service也依赖其他Service,那么我还需要对其他Service进行Mock。比如,当前 CurrentService 依赖 OtherService,那么我需要如下进行Mock:
// CurrentService.php
class CurrentService {
#[Inject]
public OtherService $otherService;
}
// OtherService.php
class OtherService {
public function aMethod() {
return [];
}
}
首先定义替身Service
protected $currentService;
protected $mockOtherService;
protected function setUp(): void
{
parent::setUp();
$this->currentService->otherService = Mockery::mock(OtherService::class);
}
如果我们在CurrenctService函数中使用到OtherService的aMethod方法,那么我们就需要如下进行mock 那么我们就需要如下进行mock
$this->currentService->otherService
->shouldReceive('aMethod')
->with(Mockery::any())
->andReturn([]);
composer run test -- test/Cases/FoobarTest.php --filter "testBar"
其中FoobarTest.php是测试文件,而testBar是需要运行的单元测试,如果不指定单元测试,则运行整个文件