How to Test Slack Notifications

Paweł Kowalski | November 26, 2020

How to Test Slack Notifications

Marketing forms are often connected to a third-party system to help the marketing team provide faster and more accurate responses.

We integrated our contact page with Slack, HubSpot, SendGrid, and emails. Whenever someone fills in the form, we notify the appropriate people via email, Slack message, and create records in HubSpot and SendGrid for future management of those contacts.

Contact form

One day we received the email, but the Slack notification did not come through, and this made me think about testing Slack notifications to catch regressions in this area.

To achieve this, we need two things:

  • A test channel for test messages
  • A test that will send the form for us

platformOS notification setup

platformOS API Call notifications support WebHooks, so that's what we used for Slack messages. The setup involves only a couple of lines of code, but the most important in the context of this article is the WebHook URL.

name: slack_landing_sales
delay: '0'
enabled: true
format: http
headers: '{ "Content-Type": "application/json" }'
request_type: POST
trigger_condition: true
"text": "
Heads up! Contact from {{ }}.
Name: {{ }} {{ }}
Email: {{ }}
Phone: {{ }}
Comments: {{ }}",

The line to change is the one with to: []() which I censored. You need your own webhook URL to be able to send messages to your channel.

To not send test data to the production Slack channel, it is a good idea to differentiate production from everything else:

to: >
  {% if contains '' %}
  {% else %}
  {% endif %}

This if condition will check if the form field called url contains a particular string. Because we are populating it before sending the form, it is filled in with the URL of the instance. In the case of our production site, it is

E2E test in TestCafe

Now, we need to programmatically go through the form, fill it, send it, and validate that it has been sent successfully.

We use TestCafe for E2E tests. It is not too difficult to start using it if you have experience with testing.

import { Selector } from 'testcafe';
import { BASE_URL } from './env';
import faker from 'faker';


test('Contact form works', async (t) => {
  const fn = Selector('[name="form[properties_attributes][first_name]"]');
  const ln = Selector('[name="form[properties_attributes][last_name]"]');
  const email = Selector('[name="form[properties_attributes][email]"]');
  const phone = Selector('[name="form[properties_attributes][phone]"]');
  const description = Selector('[name="form[properties_attributes][description]"]');
  const submitBtn = Selector('button').withText('SUBMIT');

  const notice = Selector('[role="alert"]').withText('Your contact form has been sent');

  await t
    .typeText(description, faker.lorem.paragraph(), { paste: true });


  await t.expect(notice.exists).ok();

A short summary of what is going on here:

  1. Importing TestCafe and faker (generates fake data so that the test varies every time it is run)
  2. Telling TestCafe the URL at which the test will be run
  3. Defining selectors for all the DOM elements we will be interacting with
  4. Filling in all the data in the form
  5. Submitting the form
  6. Checking if the Your contact form has been sent message is visible after the form has been sent

Running the test in chromium results in a pretty video:

test running

In your terminal, you should see something similar after running TestCafe:

> testcafe chromium tests --video artifacts --video-encoding-options r=20

 Running tests in:
 - Chrome 88.0.4295.0 / macOS 10.15.7

 ✓ Contact form works

 1 passed (38s)

This means that the test passed, so now it is time to verify if the Slack notification came through. This is what this test generated:

test results

Continuous Integration

To make our lives even more automated, we add the npm run test-ci command to our CI workflow. For this project, we use Jenkins, but you might use Github Actions, Travis, or CircleCI if you prefer. Remember that your CI server has to have a browser supported by TestCafe (see the list of officially supporter browsers here).

In our case, making tests run on every master branch run was similar to:

stage('Test on URL') {
    agent { docker { image "platformos/testcafe" } }
    environment { MP_URL = "${params.MP_URL}" }
    steps {
      sh 'npm run test-ci'
    post { failure { archiveArtifacts "screenshots/" } }

We also use a different command on CI because we want to take screenshots of failures only and run the test in a headless browser (it is much faster — the test above runs in 6 seconds instead of 38):

testcafe 'chromium:headless' --screenshots-on-fails --screenshots=screenshots tests

Now every time any code is merged into the master branch, TestCafe will test this form and trigger the Slack webhook. If the message will be missing one day, or if the test fails, we will know something is wrong either on our side or Slack's.


I hope this guide will help you avoid some of the regressions. Here are some resources that you might find useful if you decide to follow our path: