Как я использую Gmail для интеграционного тестирования

В данный момент работаю на проекте Wedding Spot. И важной частью функционала является - email.

Проблема

И несколько раз у нас были проблемы когда воркер серверы которые отправляют емейлы - падали и очень много емейлов не доходили до клиентов, или с определенным опозданием ( что не есть хорошо).
Руками тестировать это сложно ( долго/ неинтересно/ скучно).

Выбор Gmail

Так как раньше уже пробовал тестировать емейлы - и раз решил воспользоваться Gmail.
Все тесты пишу на #python #pytest + #selenium и запускаю через #travisci.

Настройка Gmail

Создаем любой Gmail аккаунт и в настройках для него включаем возможность доступа для
Less secure app access

Тестовый сценарий

Моя реализация не является идеальной и не совсем гибкой. Но она достаточно стабильная. Так что готов принять критику.
Суть одного из тестов заключается в том,

  1. Я логинюсь на наш сайт с помощью email (gmail) + password. (Couple)
  2. Открываю необходимую мне страницу
  3. Пишу сообщение.
  4. Делаю логин через #api на #gmail аккаунт пользователя которому писал сообщение (Venue)
  5. Проверяю что сообщение пришло.
  6. Отвечаю через #email
  7. Захожу на #gmail аккаунт пользователя который писал сообщение через UI (Couple)
  8. Проверяю ответ.

Реализация

Делаю очень просто ( и не очень гибко, что можно было бы переписать но пока руки не дошли).
Перед самим тестом - очищаю все входящие сообщения для двух аккаунтов (Couple + Venue )

И вот пару методов ( который нашел на просторах #stackoverflow ) и оптимизировал под свои нужды.
Первый емейл на который идет reply - должен приходить в течении минуты. По - этому реализовано все по хардкору - ждем 60 секунд и запрашиваем последний емейл.

import imaplib
import time
import email
from email.parser import Parser
import smtplib
import re
from bs4 import BeautifulSoup
SMTP_SERVER = "imap.gmail.com"
SMTP_PORT = 993


class Email(object):
    def __init__(self, email, password):
        self.login_email = email
        self.smtp_server = SMTP_SERVER
        self.password = password

    def read_last_email(self):
        """
        Get last inbox email and return it as string
        :return: string
        """
        try:
            mail = imaplib.IMAP4_SSL('imap.gmail.com')
            mail.login(self.login_email, self.password)
            mail.list()
            # Out: list of "folders" aka labels in gmail.
            mail.select("inbox")  # connect to inbox.
            result, data = mail.search(None, "ALL")
            ids = data[0]  # data is a list.
            id_list = ids.split()  # ids is a space separated string
            latest_email_id = id_list[-1]  # get the latest
            result, data = mail.fetch(latest_email_id, "(RFC822)")  # fetch the email body (RFC822) for the given ID
            raw_email = data[0][1]  # here's the body, which is raw text of the whole email
            # including headers and alternate payloads
            convert_email_to_text = email.message_from_bytes(raw_email)
            a = convert_email_to_text.get_payload()[0]
            remove_r = str(a).replace('\r', '')
            return remove_r.replace('\n', '')
        except Exception as e:
            print(str(e))

    def delete_all_email(self):
        """
        Remove all emails
        """
        mail = imaplib.IMAP4_SSL('imap.gmail.com')
        mail.login(self.login_email, self.password)
        mail.list()
        # Out: list of "folders" aka labels in gmail.
        mail.select("inbox")  # connect to inbox.
        typ, data = mail.search(None, 'ALL')
        for num in data[0].split():
            mail.store(num, '+FLAGS', '\\Deleted')
        mail.expunge()

Для второго случая нужно более гибкое ожидание.
Ждем необходимое сообщение expected_string ждем timeout и проверяем каждых poll секунд.


def wait_for_email_with_text(email, password, expected_string, timeout=150, poll=10):
    start_time = time.time()
    while (time.time() - start_time) < timeout:
        last_email = Email(email, password).read_last_email()
        if expected_string in last_email:
            return True
        else:
            time.sleep(poll)
    return False

timeout - время ожидания нужного мне емейла, а poll - как часто я проверяю.
на практике я использую timeout=7200;poll=150

Итоги

Я показал частный случай тестирования Gmail. Он имеет свои плюсы и минусы. В моем случае он полностью перекрывает все поставленные задачи. Легко настроить и быстро начать использовать.

2 лайка

Почему не взяли что-то типа GreenMail а пошли медленным и долгим путем?

не самый гибкий вариант - согласен. Забыл добавить что основное тестирование происходит на проде, как часть мониторинга системы. Возможно ошибаюсь, но GreenMail подошел бы для такого? А по поводу скорости - пытался воссоздать реальный пример работы.