sweetchuck/junit-merger
Merge multiple JUnit XML report files into a single combined report. Useful for aggregating test results from parallel jobs, multiple test suites, or split CI runs into one JUnit-compatible XML output.
Installation
composer require sweetchuck/junit-merger
Add to composer.json under require-dev if only needed for testing:
"require-dev": {
"sweetchuck/junit-merger": "^2.0"
}
First Use Case Merge two JUnit XML files (e.g., from parallel test runs):
use Sweetchuck\JUnitMerger\JUnitMerger;
$merger = new JUnitMerger();
$mergedXml = $merger->merge([
'path/to/test-results-1.xml',
'path/to/test-results-2.xml'
]);
file_put_contents('merged-results.xml', $mergedXml);
Key Files
src/JUnitMerger.php: Core logic.tests/: Example usage and edge cases.Parallel Test Merging Use in CI/CD pipelines to combine JUnit reports from parallel test workers:
$files = glob('parallel-results/*.xml');
$merged = $merger->merge($files);
Pre-Commit Hooks
Integrate with PHPUnit’s --log-junit to merge test results before upload:
$phpunitXml = 'phpunit.xml';
$merged = $merger->merge([$phpunitXml, 'custom-rules.xml']);
Dynamic File Handling Stream large files or remote reports (e.g., from GitHub Actions artifacts):
$remoteXml = file_get_contents('https://example.com/results.xml');
$merged = $merger->merge([$remoteXml, 'local.xml']);
Laravel Testing:
Use with phpunit.xml to merge test suites:
<phpunit ...>
<log>merged-results.xml</log>
</phpunit>
Then merge in phpunit.xml.dist:
$merger->merge(['tests/Unit/*.xml', 'tests/Feature/*.xml']);
CI/CD Scripts:
Add to .github/workflows/tests.yml:
- name: Merge JUnit reports
run: |
php vendor/bin/php junit-merger-cli merge results/*.xml > merged.xml
Duplicate Test IDs
If tests share the same id or name, the merger may overwrite results. Ensure unique test IDs in parallel runs:
// Example: Prefix test classes in parallel workers
class ParallelTest extends TestCase {
public function testExample() {
$this->assertTrue(true);
}
}
XML Validation Invalid XML (e.g., malformed tags) will crash the merger. Validate inputs:
$xml = simplexml_load_string($xmlContent);
if ($xml === false) {
throw new \RuntimeException("Invalid JUnit XML");
}
Memory Limits
Large files (>10MB) may hit PHP’s memory_limit. Stream files or use fopen:
$merged = $merger->merge([fopen('file1.xml', 'r'), fopen('file2.xml', 'r')]);
Verbose Output: Enable debug mode to log merged content:
$merger = new JUnitMerger(['debug' => true]);
Dry Runs: Check merged output without saving:
$mergedXml = $merger->merge(['file1.xml', 'file2.xml']);
echo $mergedXml; // Inspect before saving
Custom Merging Logic
Override the merge() method to handle edge cases (e.g., custom test attributes):
class CustomMerger extends JUnitMerger {
protected function mergeTestCases(array $testCases): array {
// Custom logic here
return parent::mergeTestCases($testCases);
}
}
Post-Merge Processing Use events to transform the merged XML (e.g., add metadata):
$merger->on('afterMerge', function ($mergedXml) {
$dom = new \DOMDocument();
$dom->loadXML($mergedXml);
// Add custom nodes
return $dom->saveXML();
});
CLI Integration
Pair with sweetchuck/junit-merger-cli for non-PHP workflows:
junit-merger merge input1.xml input2.xml > output.xml
How can I help you explore Laravel packages today?