Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[QUESTION] Code coverage with proxy class #6691

Open
rodrifarias opened this issue Apr 16, 2024 · 5 comments
Open

[QUESTION] Code coverage with proxy class #6691

rodrifarias opened this issue Apr 16, 2024 · 5 comments
Labels
question Further information is requested

Comments

@rodrifarias
Copy link

Hi!
Hyperf creates proxy classes when starting, for correct operation. However, when running tests with code coverage, some classes are not used during testing. For example:

<?php

namespace App\Controller;

use App\Service\ServiceInterface;
use Hyperf\Di\Annotation\Inject;

#[Controller('/')]
class AppController extends AbstractController
{
    #[Inject]
    private ServiceInterface $service;

    #[GetMapping('')]
    public function serviceExecute(): string
    {
        return $this->service->execute();
    }
}

Proxy class

<?php

namespace App\Controller;

use App\Service\ServiceInterface;
use Hyperf\Di\Annotation\Inject;

#[Controller('/')]
class AppController extends AbstractController
{
    use \Hyperf\Di\Aop\ProxyTrait;
    use \Hyperf\Di\Aop\PropertyHandlerTrait;
    function __construct()
    {
        if (method_exists(parent::class, '__construct')) {
            parent::__construct(...func_get_args());
        }
        $this->__handlePropertyHandler(__CLASS__);
    }

    #[Inject]
    private ServiceInterface $service;

    #[GetMapping('')]
    public function serviceExecute(): string
    {
        return $this->service->execute();
    }
}

When running the tests the proxy class is used instead of the original class, because the ClassLoader.php map classes to proxies.
Which results in invalid code coverage.

Versions:
Hyperf 3.1
PHP 8.3
PHPUnit 10.5
Xdebug 3.3

Command:

php -dxdebug.mode=coverage ./vendor/bin/co-phpunit --prepend test/bootstrap.php -c phpunit.xml --colors=always

PHPUnit XML

<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" backupGlobals="false" bootstrap="./test/bootstrap.php" colors="true" testdox="true" processIsolation="false" stopOnFailure="false" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/10.5/phpunit.xsd" cacheDirectory=".phpunit.cache" backupStaticProperties="false">
    <testsuites>
        <testsuite name="Tests">
            <directory suffix=".php">./test/Unit</directory>
        </testsuite>
    </testsuites>
    <coverage includeUncoveredFiles="true" ignoreDeprecatedCodeUnits="true" disableCodeCoverageIgnore="true">
        <report>
            <html outputDirectory="coverage" lowUpperBound="50" highLowerBound="90"/>
            <clover outputFile="./coverage/coverage.xml"/>
        </report>
    </coverage>
    <logging>
        <junit outputFile=".junit/TEST-phpunit-junit.xml"/>
    </logging>
    <source>
        <include>
            <directory suffix=".php">app</directory>
        </include>
    </source>
</phpunit>

I found a similar link on stackoverflow
https://stackoverflow.com/questions/78079124/how-to-do-unit-coverage-reporting-in-php-using-proxy-classes

I don't know how to solve this problem.

@rodrifarias rodrifarias added the question Further information is requested label Apr 16, 2024
@zds-s
Copy link
Contributor

zds-s commented Apr 28, 2024

Up to now, I haven't found a solution, which might be because I have a misunderstanding of code coverage or PHPUnit. If you have found a solution to this issue, I would greatly appreciate it if you could share it. Thank.

@GahFlorencio
Copy link

GahFlorencio commented May 9, 2024

Hello i solved this creating a ClassLoaderTest extending ClassLoader and overriding the init.
{{TEST_FOLDER}}/bootstrap.php

<?php

declare(strict_types=1);
/**
 * This file is part of Hyperf.
 *
 * @link     https://www.hyperf.io
 * @document https://hyperf.wiki
 * @contact  group@hyperf.io
 * @license  https://github.com/hyperf/hyperf/blob/master/LICENSE
 */
use Hyperf\Contract\ApplicationInterface;
use Hyperf\Engine\DefaultOption;
use HyperfTest\ClassLoaderCustom;
use Swoole\Runtime;

/*
 * This file is part of Hyperf.
 *
 * @see     https://www.hyperf.io
 * @document https://hyperf.wiki
 * @contact  group@hyperf.io
 * @license  https://github.com/hyperf/hyperf/blob/master/LICENSE
 */
ini_set('display_errors', 'on');
ini_set('display_startup_errors', 'on');

error_reporting(E_ALL);
date_default_timezone_set('America/Sao_Paulo');

Runtime::enableCoroutine(true);

! defined('BASE_PATH') && define('BASE_PATH', dirname(__DIR__, 1));

require BASE_PATH . '/vendor/autoload.php';

! defined('SWOOLE_HOOK_FLAGS') && define('SWOOLE_HOOK_FLAGS', DefaultOption::hookFlags());

require BASE_PATH.'/test/ClassLoaderCustom.php';

$classLoader = new ClassLoaderCustom();

$classLoader::init();

$container = require BASE_PATH . '/config/container.php';

$container->get(ApplicationInterface::class);

{{$TEST_FOLDER}} /ClassLoaderCustom.php

namespace HyperfTest;

use Hyperf\Di\ClassLoader;
use Hyperf\Di\ScanHandler\ScanHandlerInterface;

class ClassLoaderCustom extends ClassLoader
{
    public static function init(?string $proxyFileDirPath = null, ?string $configDir = null, ?ScanHandlerInterface $handler = null):void
    {
        if (file_exists(BASE_PATH . '/.env')) {
            static::loadDotenv();
        }
    }
}

@rodrifarias
Copy link
Author

Hi, @GahFlorencio. I also did this, but a fundamental part of the framework stops working, which is AOP. For example cache annotations. ClassLoader creates proxy classes for correct operation when there are dependencies and behaviors before or after a certain action. I also extended the ClassLoader class, I even made it load the variables from a .env.test

@GahFlorencio
Copy link

I understand, as there was no error here, I believed that it would not be a problem for testing, but it is possible that I will face new errors related to AOP

@zds-s
Copy link
Contributor

zds-s commented May 9, 2024

Perhaps it might only be possible to obtain a code coverage report for all classes, rather than a code coverage report for the entire project. Currently, I can only try to avoid using any methods that result in the creation of proxy classes. To my knowledge, there are many annotations in the Java Spring Boot framework that lead to the generation of proxy classes. Maybe someone could understand this issue by looking at the unit test reports from the Java side. I feel that it might not even be a problem at all; it's just that we might have gone down the wrong path.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Further information is requested
Projects
None yet
Development

No branches or pull requests

3 participants