此外/测试

测试

框架的结构设计旨在避免对基于此框架的代码测试设置障碍。这适用于所有类型的控制器、标准服务以及自定义的框架功能。

测试方法取决于服务的使用类型,这可以是同名类,具有静态方法格式如 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') {
    
// Successful test.
}

在这里,日志类被替换为具有相同接口的类,但其方法不会发送任何内容到日志。

假定使用了某个专门的测试库(例如 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') {
    
// Successful test.
}

现在类已通过测试且没有进行日志记录。您可以通过这种方式将任何 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') {
    
// Successful test.
}

示例展示了如何将服务状态替换为测试对象,然后恢复到初始值。 为防止在测试之外使用此方法,在生产项目中,/config/common.php 文件中的配置参数 'container.mock.allowed' 被设置为 false


#功能测试

要运行初始化框架核心的测试,您可能需要将容器中的一些或所有服务替换为测试对象。 为此,只需根据条件实现和分配您的服务(在示例中,这是全局常量 APP_TEST_ON):

<?php
// File /app/Bootstrap/BaseContainer.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';
    }
}
// TestContainer has an interface App\Bootstrap\ContainerInterface.
$config = ['container' => new TestContainer()];

$result = (new Example($config))->run();

if (
$result === 'OK') {
    
// Successful test.
}
页面翻译:chatgpt 4-o
返回顶部