Skip to content
Pro Analyzer — Available with ShieldCI Pro

Test Coverage Analyzer

Analyzer IDCategorySeverityTime To Fix
test-coverage💻 Code QualityMedium30 minutes

What This Checks

Validates that critical application modules have test coverage — using a PHPUnit/Pest coverage report when one is available, and otherwise checking each PHP class in four key directories against your tests/ directory:

  • Missing tests/ directory entirely
  • Models (app/Models) without test coverage
  • Controllers (app/Http/Controllers) without test coverage, including nested subdirectories like Api/
  • Services (app/Services) without test coverage
  • Policies (app/Policies) without test coverage

Flexible test matching: Test files don't need to exactly mirror the class name. PascalCase boundary matching handles all common Laravel naming conventions:

PatternTest fileMatches class
ExactUserTest.phpUser.php
SuffixManagePostControllerTest.phpPostController.php
PrefixUserModelTest.phpUser.php
Reverse prefixEmailVerificationTest.phpEmailVerificationPromptController.php

The reverse-prefix rule accommodates Laravel Breeze's convention of naming tests after features rather than individual controller classes.

Per-directory breakdown: Results include a per-layer summary so you can see exactly which areas need attention:

2 of 4 critical modules have a dedicated test file (50%). Models: 1/2, Controllers: 1/1, Services: 0/1

Coverage Thresholds

CoverageResultIssue Severity
>= 75%Pass-
25–74%WarningLow
< 25%WarningMedium

Why It Matters

  • Unreliable Deployments: Untested code is more likely to contain bugs that reach production
  • Regression Prevention: Without tests, code changes can break existing functionality silently
  • Confidence in Deployments: Adequate test coverage enables faster, safer deployments
  • Code Documentation: Tests serve as living documentation of expected behavior

How to Fix

Quick Fix (5 minutes)

Create your first test:

bash
# Generate a test for a specific feature
php artisan make:test UserRegistrationTest

# Generate a unit test
php artisan make:test Models/UserTest --unit

Proper Fix (30 minutes)

1. Ensure critical modules have test coverage:

php
// tests/Feature/Http/Controllers/UserControllerTest.php
use Illuminate\Foundation\Testing\RefreshDatabase;

class UserControllerTest extends TestCase
{
    use RefreshDatabase;

    public function test_index_returns_users(): void
    {
        User::factory()->count(3)->create();

        $response = $this->getJson('/api/users');

        $response->assertOk()
            ->assertJsonCount(3, 'data');
    }
}

2. Add model tests:

php
// tests/Unit/Models/UserTest.php
use Illuminate\Database\Eloquent\Relations\HasMany;

class UserTest extends TestCase
{
    public function test_user_has_orders_relationship(): void
    {
        $user = User::factory()->create();

        $this->assertInstanceOf(HasMany::class, $user->orders());
    }
}

3. Generate a coverage report for ShieldCI to read:

bash
php artisan test --coverage-clover coverage.xml

ShieldCI Configuration

The coverage report path and per-file threshold are configurable. To customize them, publish the config:

bash
php artisan vendor:publish --tag=shieldci-config

Then in config/shieldci.php:

php
'analyzers' => [
    'code-quality' => [
        'enabled' => true,

        'test-coverage' => [
            'clover_path' => 'build/reports/clover.xml', // Default: auto-discovered
            'min_file_coverage' => 80, // Default: 0 — flag critical classes below this line coverage
        ],
    ],
],

Coverage Report Discovery

When clover_path is unset, ShieldCI auto-discovers the report at coverage.xml, clover.xml, build/logs/clover.xml, coverage/clover.xml, or build/coverage/clover.xml. Generate one with php artisan test --coverage-clover coverage.xml.

References