Export JSON Test Results to CSV in GitHub Actions Pipelines
Convert JSON test results, coverage reports, and performance benchmarks to CSV format in GitHub Actions for spreadsheet analysis, trend tracking, and stakeholder reporting.
The Problem
Your CI pipeline produces JSON test results that developers can read in logs, but product managers, QA managers, and executives need the data in spreadsheet format. Converting manually after every release is tedious and error-prone.
Why This Matters
Automated test reporting closes the gap between engineering CI data and business stakeholder needs. CSV exports from GitHub Actions enable automatic test trend dashboards in Google Sheets, automated QA reports for sprint reviews, and regression detection when compared with historical data. This turns raw CI metrics into actionable business insights.
Step-by-Step Instructions
Test your JSON structure in the converter below
Paste a sample of your test output JSON into the converter. Identify which fields to include in the CSV — test name, status, duration, error message. Verify the output structure before automating.
Configure your test runner to output JSON
Most test frameworks support JSON output: pytest (--json-report plugin), Jest (--json --outputFile=results.json), Go test (go test -json). Enable this in your workflow.
Convert JSON to CSV using <code>jq</code> and <code>csvkit</code>
Use jq '[.tests[] | {name, status, duration, message}]' results.json | python3 -c "import sys,json,csv; w=csv.DictWriter(sys.stdout,['name','status','duration','message']); w.writeheader(); w.writerows(json.load(sys.stdin))"
Upload CSV as a workflow artifact or post to Google Sheets
Upload the CSV as a GitHub Actions artifact (visible in the Actions UI for 90 days) or use the Google Sheets API to append rows to a tracking spreadsheet. This builds a searchable test history.
Try It Now — JSON to CSV Converter
Open full page →All processing happens in your browser — no data is sent to any server.
Before & After Example
{
"summary": {"passed": 142, "failed": 3, "skipped": 8, "duration": 47.3},
"tests": [
{"name": "test_user_login", "status": "passed", "duration": 0.23},
{"name": "test_payment_flow", "status": "failed", "duration": 12.4,
"error": "AssertionError: Expected 200, got 503"},
{"name": "test_api_rate_limit", "status": "passed", "duration": 1.8}
]
}
# Product manager needs this in a spreadsheet
# QA manager wants trend data over time
# Manually copying is error-prone
name: Test and Report
on: [push, pull_request]
jobs:
test-and-report:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run tests with JSON output
run: |
# pytest with json-report plugin
pip install pytest-json-report
pytest tests/ --json-report --json-report-file=test-results.json
- name: Convert JSON results to CSV
run: |
python3 << 'PYEOF'
import json, csv, sys
from datetime import datetime
with open('test-results.json') as f:
data = json.load(f)
rows = []
for test in data.get('tests', []):
rows.append({
'run_date': datetime.utcnow().strftime('%Y-%m-%d'),
'commit': '${{ github.sha }}'[:8],
'branch': '${{ github.ref_name }}',
'test_name': test['nodeid'],
'status': test['outcome'],
'duration_s': round(test.get('call', {}).get('duration', 0), 3),
'error': test.get('call', {}).get('longrepr', '')[:200],
})
with open('test-results.csv', 'w', newline='') as f:
writer = csv.DictWriter(f, fieldnames=rows[0].keys())
writer.writeheader()
writer.writerows(rows)
print(f"Converted {len(rows)} test results to CSV")
PYEOF
- name: Upload CSV report as artifact
uses: actions/upload-artifact@v4
with:
name: test-results-${{ github.run_id }}
path: |
test-results.json
test-results.csv
retention-days: 90
- name: Post test summary to PR
if: github.event_name == 'pull_request'
run: |
SUMMARY=$(python3 -c "
import json
d = json.load(open('test-results.json'))
s = d.get('summary', {})
print(f"✅ {s.get('passed',0)} passed | ❌ {s.get('failed',0)} failed | ⏭ {s.get('skipped',0)} skipped | ⏱ {s.get('duration',0):.1f}s")
")
gh pr comment ${{ github.event.pull_request.number }} --body "$SUMMARY"
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
Frequently Asked Questions
Which pytest plugins output JSON test results?
pytest-json-report produces detailed JSON with test metadata. pytest-json is simpler. For JUnit XML (compatible with many CI systems), use --junit-xml=report.xml and convert to CSV with xmltodict + Python.
How do I append to a CSV over multiple runs (trend data)?
Write CSV rows to a file in a separate reports branch, or use a GitHub Actions cache key with a rolling date. Alternatively, append rows to a Google Sheet via the Sheets API using a service account secret.
Can I visualize the CSV data in GitHub itself?
GitHub renders CSV files as tables in the UI. Push your test-results.csv to a reports branch and link to it from your README. For charts, use GitHub Pages with Chart.js, or use the free tier of Grafana Cloud.
Related Workflows
Want the full JSON to CSV Converter experience?
Open the standalone tool for more space, keyboard shortcuts, and additional features.
Open JSON to CSV Converter →