Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Php Junit Merge Laravel Package

andreas-weber/php-junit-merge

A PHP tool for merging multiple JUnit XML test reports into one file, simplifying consolidation of results from parallel test runs and CI jobs. Useful for build pipelines that need a single combined JUnit report.

Deep Wiki
Context7

Getting Started

andreas-weber/php-junit-merge is a small CLI tool that concatenates multiple JUnit XML reports (the <testsuite>/<testcase> structure PHPUnit, Pest, Paratest, and Dusk emit) into a single file. That is its entire job — it is not a Laravel package, so there is no service provider, no config file, and nothing to publish.

Install it as a dev dependency so it lands next to PHPUnit:

composer require --dev andreas-weber/php-junit-merge

The binary is exposed through Composer's bin proxy:

vendor/bin/php-junit-merge --in=build/junit --out=build/junit-merged.xml

--in accepts a file or a directory of XML reports; --out is the merged result. Run vendor/bin/php-junit-merge --help first — the README is thin and flag names/behaviour are the fastest thing to verify against the version you actually installed.

First useful use case: a Laravel app that runs PHPUnit unit tests, a separate Feature suite, and Dusk browser tests in CI. Each job writes its own junit.xml; a final job merges them into one artifact that Jenkins/GitLab/SonarQube can consume. Wire it up by configuring PHPUnit logging per suite:

<!-- phpunit.xml -->
<logging>
    <junit outputFile="build/junit/unit.xml"/>
</logging>

Then point the merger at build/junit.

Implementation Patterns

Composer script for local runs. Keep the merge reproducible outside CI:

"scripts": {
    "test:merge": [
        "@php vendor/bin/php-junit-merge --in=build/junit --out=build/junit-merged.xml"
    ]
}

Clean-then-merge in CI. The most common failure is merging yesterday's reports. Always remove the input directory before the test run, and gate the merge step so it executes even when tests fail (if: always() in GitHub Actions, when: always in GitLab):

- run: rm -rf build/junit && mkdir -p build/junit
- run: php artisan test --log-junit build/junit/app.xml   # or vendor/bin/phpunit
- if: always()
  run: vendor/bin/php-junit-merge --in=build/junit --out=build/junit-merged.xml

Fan-in across a matrix. When parallel jobs each upload junit.xml as an artifact, download them into a single directory in a downstream job (actions/download-artifact with merge-multiple: true, or artifacts:paths in GitLab), then merge once. This is the pattern that makes GitLab's artifacts:reports:junit and Jenkins' JUnit plugin behave consistently, since both prefer one report per pipeline.

Parallel testing. Paratest and Pest --parallel produce per-worker files plus a wrapper report. Decide which set you want: merging the per-worker files gives per-test granularity; merging the wrapper file alongside single-process suites is usually enough. Don't merge both, or every test appears twice.

Programmatic use. The command is a standard Symfony Console command, so if you need it inside PHP (a custom Artisan command, a queue job that assembles reports) instantiate a Symfony\Component\Console\Application, register the command, and use CommandTester or Application::run(). Read src/ before doing this — the internal Merger API is not a documented public contract, so keep the coupling behind one of your own wrapper classes.

Reporting targets. Merged output feeds SonarQube's sonar.php.tests.reportPaths, Jenkins' JUnit publisher, and CodeClimate-style tooling. Keep the merged file in build/ (git-ignored) and upload it as an artifact; never commit it.

Gotchas and Tips

  • Maintenance risk is the headline. Last release was July 2021 and the package pins Symfony Console to older majors. On PHP 8.2+ you may hit deprecation noise or dependency conflicts; check composer why-not before adopting, and expect to fork if your app is on Symfony 7 components. The merge logic is small enough that a ~50-line DOMDocument implementation is a viable fallback.
  • Never point --in at your project root. Directory scanning picks up XML by extension, and phpunit.xml, phpunit.xml.dist, and composer.xml-style files will be ingested as if they were reports and produce garbage or a hard failure. Dedicate an output directory.
  • Aggregates are summed, not validated. Merged tests/failures/errors counts and durations are arithmetic on the source suites. If a report is malformed or truncated, you get wrong totals silently rather than an error.
  • Duplicates survive. The merger does not deduplicate testcases. Reruns, retries, or a stale file in the input directory inflate your counts — clean the directory, every time.
  • Coverage and properties are not merged. Clover/Cobertura coverage and <properties> blocks are out of scope; combine coverage separately (e.g., phpcov merge).
  • Empty input is the ambiguous case. An empty directory may yield an empty or invalid output file rather than a clear error. Guard with ls build/junit/*.xml in CI or check the exit code explicitly.
  • Output ordering can be non-deterministic because it follows filesystem iteration order. If you diff reports between builds, sort inputs or accept noisy diffs.
  • It loads everything into DOMDocument. Fine for thousands of tests, risky for very large Dusk suites on constrained CI runners; raise memory_limit if needed.
  • Extension points are thin by design. There is no plugin system. Prefer wrapping the binary in a Composer script or an Artisan command, and fork the command class rather than monkey-patching if you need different discovery rules or output formatting.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core