How to Send Automated Test Results to Slack
Post automated test status to Slack from GitHub Actions, or send report-aware summaries with counts and failed-test details.
To send automated test results to Slack from GitHub Actions, create a Slack incoming webhook, store its URL in a GitHub Actions secret, and use Slack’s official GitHub Action after the test step. That sends a status message and a link to the workflow run. To include pass/fail counts or failing test names, your test runner must also produce a machine-readable report, which a report-aware integration can read.
A status message and a test report are different things: a message that says a job failed does not automatically contain the runner’s test counts or failure details. This guide covers both options, secret handling, failure behavior, report handoff, troubleshooting, and alternatives.
1. Choose what you want Slack to report
| Approach | Slack receives | What you need |
|---|---|---|
| Incoming webhook status message | Job status, commit or pull request context, and a link to the run | A webhook URL stored as a repository secret |
| Report-aware notification | Counts, failed test names, or flaky test information supported by the reporter | A test report file and an integration that parses its format |
| Slack API with a bot token | A message sent to a selected channel, with API-level control | A bot token, channel ID, and the scopes required by the action |
For a fixed-channel CI alert, the incoming webhook is usually the most direct setup. Slack’s documentation describes incoming webhooks as a way to post messages from apps into Slack and supports message text and Block Kit content. The webhook is associated with a channel selected during setup. See Slack’s incoming webhook documentation.
2. Create a Slack incoming webhook and save it as a secret
- In Slack, create or configure an app for your workspace.
- Enable the
incoming-webhookpermission and install the app in the workspace. - Select the destination channel and generate the webhook URL.
- In the GitHub repository, open Settings → Secrets and variables → Actions, create a repository secret named
SLACK_WEBHOOK_URL, and paste the URL as its value.
Slack specifically instructs developers to save the generated URL as a repository secret. Do not commit it to workflow YAML, print it in logs, or include it in a message payload. Treat the URL as a credential: anyone who obtains it may be able to post to its configured destination. If it is exposed, revoke or regenerate it in Slack and replace the repository secret.
3. Post a workflow-status message
Add a workflow such as .github/workflows/tests.yml. Replace the example test command with your project’s test command. The notification step uses if: always() so it can run after a failed test step, while the job itself still fails when tests fail.
name: Tests
on:
pull_request:
push:
branches: [main]
permissions:
contents: read
jobs:
tests:
runs-on: ubuntu-latest
steps:
- name: Check out code
uses: actions/checkout@v4
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: 22
- name: Install dependencies
run: npm ci
- name: Run tests
run: npm test
- name: Notify Slack
if: always()
uses: slackapi/slack-github-action@v4.0.0
with:
webhook: ${{ secrets.SLACK_WEBHOOK_URL }}
webhook-type: incoming-webhook
payload: |
text: "Automated tests: ${{ job.status }}\n${{ github.event.pull_request.html_url || github.event.head_commit.url || github.server_url }}"
Check the Slack GitHub Action documentation for the current version and migration guidance before pinning or upgrading an action. The example uses Slack’s documented webhook, webhook-type, and payload inputs. It includes a useful status and a change link when available; it does not parse test output.
Make the message easier to act on
Include the result and a stable link to the pull request, commit, or workflow run. For events where neither a pull request URL nor a head commit URL is present, the sample falls back to the GitHub server URL; if that is not useful for your event, construct a run URL using the event context and workflow run identifiers available to your workflow. Keep the message concise and put full logs and diagnostics in the run or report.
Slack incoming webhooks accept ordinary text and Block Kit blocks. For richer formatting, replace the simple text payload with a payload containing blocks, following Slack’s message and payload documentation. Escape or safely encode dynamic values if you assemble JSON or YAML from untrusted test output; avoid inserting raw test output into a workflow expression.
4. Send counts and failing tests from a test report
A report-aware notification needs data from the test runner. Configure the runner or a reporter integration to write a machine-readable report, such as JUnit XML or CTRF JSON, then pass that file to a tool that understands the format. A workflow status alone cannot supply test counts or failed test names.
One documented pattern uses a test job to produce and upload report artifacts, followed by a notification job that downloads those artifacts. The example below shows that architecture without assuming a particular test framework or inventing a report-generation command. Replace the placeholder report command with the official reporter setup for your test runner, and choose a notification integration whose input format matches the report you generate.
name: Tests with report notification
on:
pull_request:
push:
branches: [main]
permissions:
contents: read
actions: read
jobs:
tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install dependencies
run: npm ci
- name: Run tests and write a report
run: |
# Replace with your test runner and reporter's documented command.
npm test -- --reporter=junit --outputFile=test-results/results.xml
- name: Upload test report even when tests fail
if: always()
uses: actions/upload-artifact@v4
with:
name: test-report
path: test-results/
if-no-files-found: warn
notify:
runs-on: ubuntu-latest
needs: tests
if: ${{ !cancelled() }}
steps:
- name: Download test report
uses: actions/download-artifact@v4
with:
name: test-report
path: test-results
# Add a report-aware Slack integration here. Configure it to read
# test-results/results.xml or the report format your runner produces.
The report-generation line is illustrative: reporter flags vary between test frameworks and versions. Confirm the correct command and output path in your runner’s documentation, then ensure the upload path and the notification tool’s input path agree. If the report contains sensitive data, review artifact access and retention settings for your repository before uploading it.
Use CTRF when your reporter produces CTRF JSON
CTRF’s Slack Test Results Reporter documents a command-line integration for CTRF JSON reports. Its documented commands include summary, failed-test, and flaky-test notifications, and it provides an option to send only when failures occur. It also documents JUnit-to-CTRF conversion. Follow the project’s current setup for authentication and conversion; do not point its commands at a JUnit file unless it has first been converted to the expected CTRF format.
# Examples from the CTRF reporter documentation; requires a CTRF JSON report.
npx slack-ctrf results path/to/ctrf-report.json
npx slack-ctrf failed path/to/ctrf-report.json
npx slack-ctrf flaky path/to/ctrf-report.json
npx slack-ctrf results path/to/ctrf-report.json --onFailOnly
Use the summary when the channel needs a quick overview, and failure or flaky reports when those details drive follow-up. Check the reporter’s current authentication instructions and protect any Slack credentials it requires as CI secrets.
Use a report-aware GitHub Action when its inputs fit
Automattic’s action-test-results-to-slack documents an artifact handoff pattern: upload test artifacts in the test job, download them in a notification job, and pass report paths to the action. Its documented inputs include a GitHub token, Slack bot token, channel, and reporter-specific paths. It also documents required Slack scopes and GitHub Actions read access. Verify its current maintenance state, version, supported report inputs, permissions, and repository guidance before adopting it.
For a separate notification job, make sure the notification condition permits it to run after test failures. The example pattern uses if: ${{ !cancelled() }}, which allows failure notifications while excluding cancelled runs. Decide explicitly whether cancelled runs should notify in your own workflow.
5. Pick the Slack connection that matches the workflow
| Connection | Choose it when | Credential or plan considerations |
|---|---|---|
| Incoming webhook | You need a message in a configured channel and simple setup | Store the webhook URL as a secret; Slack associates it with a selected channel |
| Slack API method through the Slack GitHub Action | You need API-based posting, a channel ID, or behavior requiring a bot | Store the bot token as a secret and grant the needed scopes |
| Slack Workflow Builder webhook | You want Slack Workflow Builder to process structured input | Slack documents that Workflow Builder webhook access requires a paid plan |
| Slack CLI | The workflow needs to run Slack CLI commands | Use the service token and CLI configuration described in Slack’s documentation |
These are the four approaches described in the Slack GitHub Action documentation. A native GitHub app can also provide workflow-run notifications in Slack, but a workflow notification is not proof that test reports have been parsed. For report contents, configure a reporter or report-aware integration.
6. Secure credentials and set permissions
- Save webhook URLs and bot tokens in GitHub Actions secrets, not source files or plain workflow variables.
- Grant only the scopes the selected integration needs. A webhook and a bot-token action have different permission models.
- For the Automattic action, its documentation lists message-writing scopes and channel-history access for updating previous messages. Its GitHub fine-grained token guidance calls for read-only Actions access. Confirm the current requirements before granting access.
- For public repositories, consider whether workflows triggered by untrusted pull requests can access secrets. Design the notification workflow around GitHub’s rules for secrets and forked pull requests.
- Do not interpolate raw user-controlled test output into shell commands or a Slack payload without appropriate escaping.
- Limit who can edit workflows that can access Slack credentials, and rotate a credential if it is accidentally exposed.
7. Reliability, performance, and cost considerations
A webhook status step adds a network call after the test work. A report-aware path adds report generation and possibly artifact upload and download, so it has more moving parts. Keep tests as the source of truth for the job result: a Slack notification failure should not silently turn a failing test run into a successful one, and a Slack outage should be visible in the workflow logs.
The Slack GitHub Action documents retry configuration for failed requests, with a default of five retries, options for other retry patterns, and an errors option that controls whether certain Slack request failures fail the step. Review the current additional configurations before changing those settings. Retries can help with transient failures, but do not make delivery guaranteed; avoid aggressive retries that create duplicate posts if the destination accepted a message but the workflow did not receive a clear response.
There is no benchmark or delivery guarantee established here. Use a concise Slack summary with a link to the run, keep bulky logs in the CI system, and send only the report detail people need. The cited setup material does not establish a Slack price for these approaches; check your workspace plan for any plan-dependent feature such as Workflow Builder webhooks.
8. Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| No Slack message after failed tests | The notify step or job only runs on success, or the notification job was skipped because its dependency failed | For a same-job step, use if: always(). For a dependent job, use a condition such as if: ${{ !cancelled() }} when failure notifications should run. Check the run’s skipped-step details. |
| The Slack message says failure but has no counts | The workflow posts job status only | Generate a machine-readable report and connect a reporter that parses that format. |
| Webhook request returns an error | The webhook URL is wrong, revoked, malformed, or not available to the run | Check the secret name and value in repository settings. Regenerate an exposed or revoked URL, update the secret, and avoid printing it in logs. |
| Bot token request is unauthorized | The token is invalid, the app is not installed, or a required scope is missing | Check the bot token secret, install the app in the workspace, grant only the required scopes, and reauthorize if needed. |
| Message cannot be posted to the channel | The channel ID is incorrect or the bot lacks access to the channel | Verify the channel ID and invite the bot or grant the specific documented permission needed by the integration. |
| Report-aware step says no report was found | The test command did not create the expected file, or the artifact and consumer paths differ | Inspect the test step output, verify the reporter’s output path, and align upload, download, and parser paths. |
| Notification job cannot find artifacts | The upload step did not run after failure, or artifact names/paths do not match | Use an upload condition that runs after test failure, check the artifact name, and download it to the directory expected by the parser. |
| Slack step warns but the workflow remains green | The Slack action’s error behavior may be configured not to fail on request errors | Review the action’s errors setting and ok output. Decide whether a notification failure should fail the job or remain a warning. |
| Duplicate or confusing notifications | Every matrix job or rerun posts independently, or both an app subscription and workflow post are enabled | Choose a single notification owner, aggregate matrix results if needed, and include the run or commit identifier in messages. |
| Forked pull requests receive no notification | Repository secrets are not available to untrusted fork workflows under GitHub’s security behavior | Use a workflow design that respects GitHub’s secret-access rules. Do not expose Slack credentials to untrusted code to force a notification. |
9. Or skip the browser setup
For screenshot capture in a test or CI workflow, ScreenshotNeo is a website screenshot API and MCP server for developers. It returns a PNG, JPEG, WebP, or PDF from one GET request. Its API can help when a test workflow needs a page capture, while Slack remains the channel for notifying the team. See the ScreenshotNeo API documentation for parameters and setup.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
ScreenshotNeo removes cookie banners, newsletter popups, and chat widgets before capture; bot checks, blank pages, and failed loads are never billed. Its MCP server lets AI agents take screenshots. The free plan includes 1,000 screenshots per month with no card, and paid plans start at $5 for 3,000 screenshots.
Sign up for ScreenshotNeo’s free plan: 1,000 screenshots a month, no card required.
FAQ
Will this work with a test runner other than Node.js?
Yes. The Slack notification is separate from the test command. Run your framework’s tests in CI, then either send a status message or generate a report in a format supported by your chosen reporter.
Can an incoming webhook update or delete a previous message?
Incoming webhooks are suited to posting messages to their configured destination. If you need API behavior such as updating messages, choose a bot/API integration and check its required scopes and methods.
Should Slack notifications run for every passing build?
That depends on the channel’s purpose. A team channel may need every result, while a failure-only alert can reduce noise. For failure-only reporting, use a report-aware tool’s documented option or add an explicit condition based on the test result.
Where should the full test output live?
Keep detailed output and artifacts in the CI run or report storage. Slack is most useful as a summary with a direct link to the run and enough context to identify the affected change.
References: Slack incoming webhooks; Slack GitHub Action; Slack Action additional configurations; Automattic test-results-to-slack; CTRF Slack test reporter.


