TestVagrant

Integrating Slack for Test Reporting using Python

Blog

Integrating Slack for Test Reporting using Python

Publishing results of automated test execution is as vital as running the tests continuously. This helps in building visibility and credibility of test automation within fast-paced agile teams. One of the primary methods in the industry of publishing test reports is to send automated emails with attached reports post each test run.

However, creating & maintaining the code for sending email notifications is time-consuming and tedious. Moreover, sending emails for highly frequent test runs would, almost, be like spamming inboxes and hence, eventually get ignored.

So, we tried to tackle this problem by automating the process of sending the reports using one of the most popular communication medium used across the industry — SLACK.

Now we need to address some questions to facilitate the implementation:

  1. How do we establish a connection with Slack?
  2. How can we send the notifications to Slack?

How do we establish a connection with Slack?​

Basically, we can achieve this using the Incoming Webhooks added for respective Slack workspaces

The diagram below shows the workflow of Slack Webhook URL creation; refer to this page for the steps involved in the process.

The following chart shows what happens once we obtain the Webhook URL:

Now let’s address the second question we mentioned above:

How can we send the notifications to Slack ?

We can push the notification to slack using the POST network call with payload representing the slack message.

For network calls to be made, we used the requests package in python, Requests is an elegant and simple HTTP library for python.

 

Slack provides us with a variety of message formats that can be used to compose our own slack message template as required.

Now the question arises,

When to push the notifications?

Post Execution of all test cases.

Where do we obtain the results required to be embedded within the message?

Generated allure report

Enough of theory and let’s get our hands dirty with code. We have divided the complete code into 3 sections, namely

1. Slack Helper Methods — utility functions

				
					from datetime import date

class SlackHelperMethods:

    @classmethod
    def __get_current_date():
        """
        Returns:
            Date with following format - DD-MM-YYYY
        """
        today = date.today()
        return f"{today.day}-{today.month}-{today.year}"

    @classmethod
    def __convert_time_from_milli(millis):
        """
        Args:
            millis -> milli-seconds
        Returns:
            Converted time of milli seconds to hour-minute-second format - 1h 03m 23s
        """
        construct_time = ""

        seconds = int((millis/1000) % 60)
        minutes = int((millis/(1000*60)) % 60)
        hours = int((millis/(1000*60*60)) % 24)

        for [time, param] in zip([hours, minutes, seconds], ["h", "m", "s"]):
            if time == 0:
                continue
            else:
                construct_time += f"{time}{param} "
        if construct_time == "":
            return "Time is less than a second"
        return construct_time.strip()

    @classmethod
    def __helper_to_extract_module_wise_failed_count(cls):
        if len(SlackTemplate.RESULTS['module_wise_failure']) == 0:
            return "No Module Wise Failures Found"

        resulting_string = ""
        index = 1

        for key, value in SlackTemplate.RESULTS["module_wise_failure"].items():
            resulting_string += f"{index+1}. {key}: {value}\n"

        return resulting_string

    @classmethod
    def __get_results_from_allure_report(cls):
        # Implementation to extract the details from allure report (or any other reporting tool that you might be utilizing)
 
				
			

2. Slack Template — header and body constructed templates

				
					class SlackTemplate:

    # The following data is extracted from Allure Reports ( NOT HARD CODED )
    RESULTS = SlackHelperMethods.__get_results_from_allure_report()

    @classmethod
    def __slack_header(cls):
        return [
            {
                "type": "header",
                "text": {
                        "type": "plain_text",
                        "text": f"ENV - {SlackTemplate.RESULTS['env']} - Test Execution Summary ( {SlackTemplate.RESULTS['platform']} ) - {SlackHelperMethods.__get_current_date()}",
                }
            }
        ]

    @classmethod
    def __slack_body(cls):
        return [
            {
                "text": f"Passed Count: *{SlackTemplate.RESULTS['passed']}*",
                        "mrkdwn_in": [
                            "text"
                        ],
                "color": "good"
            },
            {
                "text": f"Broken Count: *{SlackTemplate.RESULTS['broken']}*",
                        "mrkdwn_in": [
                            "text"
                        ],
                "color": "warning"
            },
            {
                "text": f"Failed Count: *{SlackTemplate.RESULTS['failed']}*",
                        "mrkdwn_in": [
                            "text"
                        ],
                "color": "danger"
            },
            {
                "text": "",
                        "footer": f"_Total Execution Time: *{SlackHelperMethods.__convert_time_from_milli(SlackTemplate.RESULTS['total_execution_time'])}*_",
                        "color": "#87ceeb",
                        "mrkdwn_in": [
                            "footer"
                        ]
            },
            {
                "title": "Module Wise Failed / Broken Count",
                "color": "danger",
                "text": f"{SlackHelperMethods.__helper_to_extract_module_wise_failed_count()}",
                        "mrkdwn_in": [
                            "text"
                        ]
            }
        ]

    @staticmethod
    def constructed_message_format():
        return {
            "blocks": SlackTemplate.__slack_header(),
            "attachments": SlackTemplate.__slack_body()
        }
				
			

3. Slack Notification — final push

				
					import requests
import json

WEBHOOK_URL = "https://hooks.slack.com/services/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"

class SlackNotification:

    @staticmethod
    def send_slack_notification():
        response = requests.post(
            WEBHOOK_URL, data=json.dumps(
                SlackTemplate.constructed_message_format()),
            headers={'Content-Type': 'application/json'}
        )
        return response

if __name__ == "__main__":
    SlackNotification.send_slack_notification()
				
			

Below is the final message sent which contains details like environment (QA, PRE-PROD or PROD), platform name (API, Mobile or Web), number of test cases passed, failed & broken, total time of execution, date of execution, module wise test case failure count that were extracted from reports

With this approach, we were successful in sending the Slack message to the respective channels using the Webhooks that connects our test framework and slack.

Hope our blog helps and reduces the time to spend on integrating Slack with your application.

Happy Learning!!!

Other Related Articles

Scroll to Top