此外/测试
测试
框架的结构设计旨在避免对基于此框架的代码测试设置障碍。这适用于所有类型的控制器、标准服务以及自定义的框架功能。
测试方法取决于服务的使用类型,这可以是同名类,具有静态方法格式如 Hleb\Static\Service::method(),用于框架内置服务,或使用 DI,后者是将服务(及其他对象)注入到类的方法和构造函数中。
在框架内,Dependency Injection仅适用于由框架创建的对象,包括控制器、middleware、命令、事件以及由名为DI的服务创建的对象。
#Dependency Injection 测试
这是一个使用 DI 的示例控制器:
<?php
namespace App\Controllers;
use Hleb\Base\Controller;
use Hleb\Reference\Interface\Log;
class ExampleController extends Controller
{
public function index(Log $logger): string
{
$logger->info('Request to demo controller');
return 'OK';
}
}
假设您需要确保控制器返回文本'OK',但不会发送消息到日志。
use App\Controllers\ExampleController;
use Hleb\Main\Logger\NullLogger;
$controller = new ExampleController();
$logger = new NullLogger();
$result = $controller->index($logger);
if ($result === 'OK') {
}
在这里,日志类被替换为具有相同接口的类,但其方法不会发送任何内容到日志。
假定使用了某个专门的测试库(例如 github.com/phhleb/test-o),并通过其实现检查。
现在,通过 DI 服务(具体是框架服务,而不是架构方法)调用任意类的方法:
use Hleb\Reference\Interface\Log;
class Example
{
public function run(Log $logger): string
{
$logger->info('Demo class method executed');
return 'OK';
}
}
use Hleb\Static\DI;
$result = DI::method(new Example(), 'run');
在这种情况下,日志服务将从容器中注入,消息将记录到日志中。我们将修改方法调用以进行测试:
use Hleb\Main\Logger\NullLogger;
use Hleb\Static\DI;
$result = DI::method(new Example(), 'run', ['logger' => new NullLogger()]);
if ($result === 'OK') {
}
现在类已通过测试且没有进行日志记录。您可以通过这种方式将任何 DI 对象替换为自定义类,从而便于测试。
#标准服务测试
HLEB2 框架的内置服务可以通过静态方法 Hleb\Static\Service::method() 访问。
这种方法简化了对服务的访问,但可能会使包含它们的模块的测试变得复杂,尽管仍然可行。以下是日志记录的示例:
use Hleb\Static\Log;
class Example
{
public function run(): string
{
Log::info('Demo class method executed');
return 'OK';
}
}
use Hleb\Main\Logger\NullLogger;
use Hleb\Init\ShootOneselfInTheFoot\LogForTest;
$logger = new NullLogger();
LogForTest::set($logger);
$result = (new Example())->run();
LogForTest::cancel();
if ($result === 'OK') {
}
示例展示了如何将服务状态替换为测试对象,然后恢复到初始值。
为防止在测试之外使用此方法,在生产项目中,/config/common.php 文件中的配置参数 'container.mock.allowed' 被设置为 false。
#功能测试
要运行初始化框架核心的测试,您可能需要将容器中的一些或所有服务替换为测试对象。
为此,只需根据条件实现和分配您的服务(在示例中,这是全局常量 APP_TEST_ON):
<?php
namespace App\Bootstrap;
use Hleb\Constructor\Containers\CoreContainer;
final class BaseContainer extends CoreContainer implements ContainerInterface
{
private ?ContainerInterface $testContainer = null;
#[\Override]
final public function get(string $id): mixed
{
if (get_constant('APP_TEST_ON')) {
if ($this->testContainer === null) {
$this->testContainer = new TestContainer();
}
return $this->testContainer->get($id);
}
return ContainerFactory::getSingleton($id) ?? match ($id) {
default => parent::get($id),
};
}
}
#测试内置功能
框架中多项简化服务调用的内置功能,例如 logger() 函数,通过可测试的服务调用实现,在这种情况下是对 Hleb\Static\Log 的包装。
#类中的$this-container测试
在控制器、middlewares、命令、事件以及从 Hleb\Base\Container 继承的其他类中,可以像 $this-container 这样访问容器。
如果您选择这种使用容器的方法(在项目中混合使用不同的方法会显得奇怪),则需要特别初始化对象构造函数以进行测试。
use Hleb\Base\Container;
use Hleb\Reference\LogInterface;
class Example extends Container
{
public function run(): string
{
$this->container->get(LogInterface::class)->info('Demo class method executed');
return 'OK';
}
}
$config = ['container' => new TestContainer()];
$result = (new Example($config))->run();
if ($result === 'OK') {
}
页面翻译:chatgpt 4-o