Tangible Bytes

A Web Developer’s Blog

Vitest and PhpUnit Code Coverage Reports for Laravel to SonarQube

For some reason I always seem to struggle getting code coverage reports picked up by SonarQube - the documentation isn’t bad but it seems to take me a while to find the right incantation .

I wanted both my PhpUnit tests and Javascript unit tests for my laravel project to generate coverage reports and import this to SonarQube.

My vite config


import { sentryVitePlugin } from "@sentry/vite-plugin";
import { defineConfig } from "vite";
import laravel from "laravel-vite-plugin";
import react from "@vitejs/plugin-react";

export default defineConfig({
    plugins: [
        laravel({
            input: "resources/js/app.jsx",
            refresh: true,
        }),
        react(),
        sentryVitePlugin({
            org: "myorg",
            project: "myproject",
        }),
    ],

    build: {
        sourcemap: true,
    },
    test: {
        environment: "jsdom",
        setupFiles: ["./resources/js/tests/setup.js"],
        globals: true,
        resetMocks: true,
        coverage: {
            enabled: true,
            include: "resources/js",
            reporter: ["html", "lcov"],
            reportsDirectory: "public/vitest-coverage/",
        },
    },
    server: {
        origin: "http://localhost:5173",
        cors: {
            origin: "http://localhost",
            methods: "GET,HEAD,PUT,PATCH,POST,DELETE",
            preflightContinue: false,
            optionsSuccessStatus: 204,
        },
        open: false,
    },
    resolve: {
        alias: {
            "@": "/resources/js",
        },
    },
});

in package.json I have

    "scripts": {
        "dev": "vite",
        "build": "vite build",
        "test": "vitest --ui",
        "coverage": "vitest run --coverage"
    },

This provides both an html report viewable in the vite server via localhost and an lcov report on the filesystem

My phpunit.xml file

<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="vendor/phpunit/phpunit/phpunit.xsd"
         bootstrap="vendor/autoload.php"
         colors="true"
>
    <testsuites>
        <testsuite name="Unit">
            <directory>tests/Unit</directory>
        </testsuite>
        <testsuite name="Feature">
            <directory>tests/Feature</directory>
        </testsuite>
    </testsuites>
    <source>
        <include>
            <directory>app</directory>
        </include>
    </source>
    <php>
        <env name="APP_ENV" value="testing"/>
        <env name="BCRYPT_ROUNDS" value="4"/>
        <env name="CACHE_DRIVER" value="array"/>
        <env name="DB_DATABASE" value="testing"/>
        <env name="MAIL_MAILER" value="array"/>
        <env name="PULSE_ENABLED" value="false"/>
        <env name="QUEUE_CONNECTION" value="sync"/>
        <env name="SESSION_DRIVER" value="array"/>
        <env name="TELESCOPE_ENABLED" value="false"/>
    </php>
</phpunit>

Then I use this script to run all the tests and import to Sonar - I run Laravel in Docker via Sail and run Sonarqube in Docker

#!/bin/bash

./vendor/bin/sail test --coverage-clover=coverage.xml --coverage-html=public/coverage-report
./vendor/bin/sail npm run coverage

sonar-scanner \
  -Dsonar.projectKey=cms_laravel \
  -Dsonar.sources=app,resources/js/Components,resources/js/Layouts,resources/js/Pages,resources/js/app.jsx,resources/js/bootstrap.js \
  -Dsonar.tests=tests,resources/js/test,resources/js/tests \
  -Dsonar.host.url=http://localhost:9000 \
  -Dsonar.php.coverage.reportPaths=coverage.xml \
  -Dsonar.javascript.lcov.reportPaths=public/vitest-coverage/lcov.info \
  -Dsonar.token=$(cat sonar-token)

Now it all works beautifully and it is really helping me keep my code quality where I want it.