sebastianbergmann / php-code-coverage

Library that provides collection, processing, and rendering functionality for PHP code coverage information.
BSD 3-Clause "New" or "Revised" License
8.82k stars 375 forks source link

Incorrect Code Coverage Report? #1044

Closed aldobarr closed 2 weeks ago

aldobarr commented 2 months ago
Q A
php-code-coverage version 11.0.6
PHP version 8.3.7
Driver Xdebug
Xdebug version (if used) 3.3.2-1
Installation Method Composer
Usage Method PHPUnit
PHPUnit version (if used) 11.3.1

I'm currently using Laravel v11.21.0. In it I have a service which interacts with an API using Laravel's Http Facade. Here's the code:

<?php

declare(strict_types=1);

namespace App\Services;

use Illuminate\Http\Client\ConnectionException;
use Illuminate\Http\Client\PendingRequest;
use Illuminate\Http\Client\RequestException;
use Illuminate\Support\Facades\Http;
use Psr\Http\Message\ResponseInterface;

class ApiService
{
    private PendingRequest $client;
    private const RETRY_ATTEMPTS = 3;

    public function __construct(private readonly string $baseUrl, private readonly string $apiToken)
    {
        $this->resetClient();
    }

    protected function resetClient(): void
    {
        $this->client = Http::baseUrl($this->baseUrl)
            ->retry(self::RETRY_ATTEMPTS, $this->getRetryDelay(...))
            ->withResponseMiddleware(function (ResponseInterface $response) {
                $this->resetClient();
                return $response;
            })->withHeader('api-key', $this->apiToken)->acceptJson();
    }

    public function getRetryDelay(int $attempt, \Exception $exception): int
    {
        return pow(2, $attempt - 1) * 100;
    }

    public function createCustomer($data): object
    {
        $response = $this->client->post('v1/endpoint', [
            'data' => $data
        ])->throw();

        $response_object = $response->object();
        if ($response_object->result !== 'success') {
            throw new RequestException($response);
        }

        return $response_object->data;
    }
}

For whatever reason this API will respond with 200 status codes sometimes even when invalid data is sent... Anyway, I have this unit test for covering that case:

    #[Test]
    public function error_response_with_200_status(): void
    {
        $url = 'https://example.com';
        $key = 'api-key';

        Http::preventStrayRequests();
        Http::fake([
            $url . 'v1/endpoint' =>
                Http::response(json_encode(['result' => 'error', 'data' => (object)[]]), Response::HTTP_OK)
        ]);

        $this->expectException(RequestException::class);
        $apiService = new ApiService($url, $key);
        $apiService->createCustomer([]);
    }

I've stepped through this code and confirmed 100% that the line within the result !== 'success' if statement is executed. Yet here's a code coverage report for the ApiService:

image

I also tested this with PCOV v1.0.11-5 and got the same results...

sebastianbergmann commented 2 months ago

Thank you for your report.

Please provide a minimal, self-contained, reproducing test case that shows the problem you are reporting.

Without such a minimal, self-contained, reproducing test case I will not be able to investigate this issue.

aldobarr commented 2 months ago

Thank you for your report.

Please provide a minimal, self-contained, reproducing test case that shows the problem you are reporting.

Without such a minimal, self-contained, reproducing test case I will not be able to investigate this issue.

I did, see above.

sebastianbergmann commented 2 months ago

The example you provided is neither minimal nor self-contained.

sebastianbergmann commented 2 weeks ago

No feedback, closing.