0

I have a page and test class, the test class looks something like below

import unittest
import pytest
import logging

@pytest.mark.usefixtures("setup")
class BigRandomTest(unittest.TestCase):
    log = cl.testogger(logging.DEBUG)

    @pytest.fixture(autouse=True)
    def classSetup(self):
        self.synMax = CarMaxSyndicated()

    # Standard set of tests
    @pytest.mark.run(order=1)
    def test_column_uniqueness_dealership(self):
        pass
        self.log.debug('List of all column duplicate counts: \n' + str(result[1]))
        assert result[0].empty, 'Test failed. Columns with a suspect amount of duplicates: \n {}'.format(result[0])
    
    @pytest.mark.run(order=2)
    def test_column_uniqueness_consistency_dealership(self):
        pass
        self.log.debug('List of all column duplicates differences: \n' + str(result[1]))
        assert result[0].empty, 'Test failed. Columns with significantly different duplicate counts between collections: \n {}'.format(result[0])

when I run the pytest -s -v path_to_test --html=result.html --self-contained-html it generates the report but what I want to do is email the report whenever there is a test failure only, I can setup some stmp or if there is a package that I can use it would be helpful.

Ronron
  • 69
  • 1
  • 2
  • 11

1 Answers1

1

Here is what the full code for conftest.py would look like for this situation:

conftest.py

import pytest
import smtplib
import ssl
from email import encoders
from email.mime.base import MIMEBase
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText

failed: bool = False


def send_email():
    """
    function to send an email with attachment after all test are run and a failure was detected
    """
    subject: str = "subject"
    body: str = "body"
    sender_email: str = "email"
    receiver_email: str = "email"
    password: str = "password"  # Recommended for mass emails# This should obviously be an input or some other stronger protection object other than a string
    smtp: str = "smtp"
    filename: str = "result.html"
    port: int = 587

    # Attachments need to be multipart
    message = MIMEMultipart()
    message["From"] = sender_email
    message["To"] = receiver_email
    message["Subject"] = subject
    message["Bcc"] = receiver_email
    # Add body to email
    message.attach(MIMEText(body, "plain"))

    # Open html file in binary mode
    # This will only work if HTML file is located within the same dir as the script
    # If it is not, then you will need to modify this to the right path
    with open(filename, "rb") as attachment:
        # Add file as application/octet-stream
        # Email client can usually download this automatically as attachment
        part = MIMEBase("application", "octet-stream")
        part.set_payload(attachment.read())

    # Encode file in ASCII characters to send by email
    encoders.encode_base64(part)

    # Add header as key/value pair to attachment part
    part.add_header(
        "Content-Disposition",
        f"attachment; filename= {filename}",
    )

    # Add attachment to message and convert message to string
    message.attach(part)
    text = message.as_string()

    # Log in to server using secure context and send email
    context = ssl.create_default_context()
    with smtplib.SMTP(smtp, port) as server:
        server.starttls(context=context)
        server.login(sender_email, password)
        server.sendmail(sender_email, receiver_email, text)


@pytest.hookimpl(tryfirst=True, hookwrapper=True)
def pytest_runtest_makereport(item, call):
    global failed
    report = yield
    result = report.get_result()

    if result.when == 'call' and result.outcome == "failed":
        failed = True
        print("FAILED")


def pytest_sessionfinish(session, exitstatus):
    if failed is True:
        send_email()

There are two key functions within this file.

  • pytest_runtest_makereport
  • pytest_session

pytest_runtest_makereport will run after every test and simply check if the test failed or not. If any test fails, then the failed flag will be set to True.

Then at the end of the whole script, pytest_session will run and check if the flag was changed to True. If it was, then an email will be sent with the result.html file attached.

I would like to highlight that part of this question was previously answered before, see the link below: How to send pytest coverage report via email?

Given the linked question/answer did not contain any information on how to only run if a test failed. I decided it was worth answering here.

Ryan Burch
  • 500
  • 4
  • 11