Привет, Хабр! Меня зовут Анастасия Валеева, я – руководитель группы обеспечения качества в Tele2. Наша команда работает в большинстве своём с SAP ERP, и мы не понаслышке знаем, что автоматизация данной платформы — дело далеко не тривиальное. В этой статье я хочу поделиться с вами, как и зачем мы автоматизировали тестирование с помощью Python.

Зачем мы это придумали 

SAP ERP – гибкий инструмент в руках нашей команды. Мы дорабатываем функциональность системы под потребности конкретного бизнес сегмента. Эти изменения производятся по запросу бизнес-пользователей. Объём и влияние доработок могут быть различными, но одно остаётся неизменным – каждая доработка является уникальной. Таким образом, это не простое устранение багов и улучшения текущего функционала, не изменение версионности продукта после оптимизации, а, как правило, абсолютно новый «продукт» в системе. В случае автоматизации функционального тестирования нам потребуется писать автотест на каждую доработку/разработку, что занимает больше времени, чем ручное тестирование (написание автотеста, отладка, оптимизация) + данный автотест с каждой новой разработкой будет уже неактуален, и нужно будет создавать новые и новые из раза в раз. Делаем выводы, что автоматизировать функциональные тексты для нас нерелевантно. А вот регрессионные тесты, которые мы проводим после каждого изменения системы, представляют собой более шаблонные варианты, шаги повторяются, и от их автоматизации есть профит.

Сейчас мы работаем с SAP ERP и интегрированными продуктами (FileNet, BW, Fiori), однако, импортозамещение идёт полным ходом, и мы проводим пилотный проект по миграции на новую платформу. Так или иначе, созданный нами инструмент для автотестов универсален и может быть применён в работе с новой системой.

Как выбирали инструмент автоматизации

Из множества инструментов автоматизации мы выбрали для ознакомления четыре наиболее совместимых с SAP ERP:

  • SAP Scripting;

  • Tricentis Tosca;

  • eCatt;

  • CBTA.

Анализируя, мы исходили из трёх основных для нас факторов: скорость освоения, простота и гибкость, а также бюджет. По каждому из инструментов мы отметили свои плюсы и минусы, собрали информацию в единую таблицу. И вот что у нас получилось.

По количеству зелёных блоков мы увидели, что нашим критериям в большей степени соответствует SAP Scripting.

Принцип работы данного инструмента состоит в том, что он записывает все действия пользователя в системе, на выходе формирует файл в формате .vbs, который в последующем можно запускать в SAP. Соответственно, при запуске этого файла система будет повторять ваши предварительно записанные шаги. Кроме того, данный файл можно корректировать: удалять лишнее, дописывать недостающее или даже полностью переписать. Для этого необходимо открыть файл либо в блокноте, либо в любом другом редакторе, работающем с кодом.

В процессе пилотирования SAP Scripting помимо технических вопросов мы решали несколько административных задач: удобство использования, гибкость, кастомизация, универсальность, прозрачность.

Мы хотели внедрить такой инструмент, который будет полезен не только группе тестирования, но и другим смежным группам нашего подразделения. И поскольку мы говорим об автоматизации, одним из основополагающих факторов для нас было минимальное участие человека в этом процессе. Согласитесь, часто хочется просто нажать на волшебную кнопку "РАБОТАТЬ", чтобы оно всё само заработало :)

Добиться данного магического эффекта «работает само» нам помог Python. За это отвечала коллега из моей команды — она написала скрипт для робота, который сейчас работает буквально по одному клику.

Что касается прозрачности, то мы пошли по пути, доступному для любого пользователя. Для этого «прикрутили» Python к файлу Excel. Это означает, что сейчас провести регресс может любой сотрудник — достаточно зайти в файл автотеста и нажать кнопку «СТАРТ».

Как это работает

Бизнес-процесс состоит из набора бизнес-операций. Например, создание логистического заказа состоит из заведения заказа, смены статуса подписания договора, деблокирования заказа и создания счёта-фактуры. Для обеспечения полного регрессионного тестирования мы автоматизируем всю цепочку шагов. На выходе получаем Excel-документ со скриншотами и подробной информацией по каждому шагу тестирования. Причём регресс может запустить любой пользователь, не только тестировщик, это доступно в том числе для менеджеров со стороны бизнеса. А полученные данные (скрипты) можно использовать также для генерации тестовых данных.

Существует несколько способов выполнения автотестов.

1. Отдельно по каждому бизнес-процессу.
По каждому модулю финансовой системы SAP ERP создан файл Excel, в котором есть кнопка вызова макроса. По вызову этой кнопки запускается Visual Basic for Applications. VBA обращается к системе SAP и вызывает на выполнение ранее записанный скрипт vbs.Таким образом, мы можем выполнять тестирование по отдельному модулю или бизнес-операции.

2. По всему модулю или нескольким модулям.
Для этих нужд как раз используется Python. Наш робот обращается к SAP, открывая рабочее окно. Далее вызывает необходимые файлы Excel, которые работают по описанному принципу макросов на VBA. Таким образом, мы получаем следующую цепочку:

При этом пользователю необходимо только единожды нажать кнопку ВЫПОЛНИТЬ.

Запуск SAP GUI

#Запуск SAP GUI
import sys
import datetime
import time
    
import win32com.client
import win32gui
import win32process
import win32api
import win32con
    
from threading import Thread
class Log:
    def __init__(self, text=''):
        self.__logs = text
    @property 
    def logs(self):
        return self.__logs
    @logs.setter
    def logs(self, text):
        self.__logs = self.__logs + text + '\\n'
class System_check:
    def __init__(self, flag=False):
        self.__flag = flag
    @property
    def flag(self):
        return self.__flag
    @flag.setter
    def flag(self, value):
        self.__flag = value
    
def check_EAP():
    inplay_children = []
    hwnd_EAP = 0
    while len(inplay_children) == 0: 
        wnd = win32gui.FindWindow(None,\"Microsoft Excel\") 
    
        def is_win_ok(hwnd, *args):
            if wnd != 0 and win32gui.GetParent(hwnd) == wnd:
                s = win32gui.GetWindowText(hwnd)

                if \"EAP\" in s: 
                    nonlocal hwnd_EAP
                    hwnd_EAP = win32gui.GetParent(hwnd)
                    EAP.flag = True
    
                if \"ОК\" in s and win32gui.GetParent(hwnd) == hwnd_EAP:
                    log = 'система EAP. Выполнение скрипта прервано'
                    print(log)
                    inplay_children.append(hwnd)
                    apply_button = hwnd
                    win32api.PostMessage(apply_button, win32con.WM_LBUTTONDOWN, 0, 0)
                    win32api.PostMessage(apply_button, win32con.WM_LBUTTONUP, 0, 0)
                    win32api.PostMessage(apply_button, win32con.WM_LBUTTONDOWN, 0, 0)
                    win32api.PostMessage(apply_button, win32con.WM_LBUTTONUP, 0, 0)
                    text.logs = log + '\\n'
                    return 0
        if wnd != 0:
            parent_sap = wnd   
            win32gui.EnumChildWindows(parent_sap, is_win_ok, None)
        time.sleep(1)
def func_logon_sap_launch_script(): 
    inplay_children = []
    time.sleep(2)
    while len(inplay_children) == 0: 
        wnd = win32gui.FindWindow(None,\"SAP Logon\") 
        def is_win_ok(hwnd, *args):
            if wnd != 0 and win32gui.GetParent(hwnd) == wnd:
                s = win32gui.GetWindowText(hwnd)
                if \"OK\" in s: 
                    inplay_children.append(hwnd)
        if wnd != 0:
            parent_sap = wnd   
            win32gui.EnumChildWindows(parent_sap, is_win_ok, None)
            if len(inplay_children) != 0:
                time.sleep(3)
                apply_button = inplay_children[0]
                win32api.PostMessage(apply_button, win32con.WM_LBUTTONDOWN, 0, 0)
                win32api.PostMessage(apply_button, win32con.WM_LBUTTONUP, 0, 0)
        time.sleep(1)

def close_security():
    i = 0
    while i < 10:
        wnd1 = win32gui.FindWindow(None,\"SAP GUI Security\")
        wnd2 = win32gui.FindWindow(None,\"Безопасность SAP GUI\")

        if wnd1:
            wnd = wnd1
        else:
            wnd = wnd2
    
        inplay_children = []

        def is_win_ok(hwnd, *args):
            if wnd != 0 and win32gui.GetParent(hwnd) == wnd:
                s = win32gui.GetWindowText(hwnd)
                if \"Allow\" in s: 
                    inplay_children.append(hwnd)

        if wnd != 0:
            parent_sap = wnd   
            win32gui.EnumChildWindows(parent_sap, is_win_ok, None)
            apply_button = inplay_children[0]
            win32api.PostMessage(apply_button, win32con.WM_LBUTTONDOWN, 0, 0)
            win32api.PostMessage(apply_button, win32con.WM_LBUTTONUP, 0, 0)    
        i += 1
        time.sleep(5)

Заведение функции для чтения файла Excel

#Заведение функции для чтения файла Excel
===
def function_doc(book, sheets_order_launch, path, Excel):
    backslash = book.rfind('\\\\') + 1 
    path += book[:backslash]
    book = book[backslash:]
    module = book[:-5]             
    wb = Excel.Workbooks.Open(path+book)
    try:
        for sheet in sheets_order_launch: 
            tl =  Thread(target=func_logon_sap_launch_script)
            tl.start()

            tEAP = Thread(target=check_EAP)
            tEAP.start()
            if sheet == 'ZFDALLLOAD':
                #мониторится появление окна Security и подтверждение загрузки файла
                th = Thread(target=close_security)
                th.start()

            pattern = book + '!' + sheet + '.' + sheet + '_Button_Click'
            log = 'запущен ' + book + ' ' + sheet + ' в ' + str(datetime.datetime.now().strftime(\"%d/%m/%Y %H:%M:%S\"))
            print(log)
            text.logs = log + '\\n'
            Excel.Run(pattern) 

    except Exception as e:
        log = f'Возникло исключение: {e} \\nПроблема при выполнении листа {sheet}, файл сохранен'
        print(log)

        text.logs = log + '\\n'
    finally:

        now = str(datetime.datetime.today().replace(microsecond=0)).replace(':', '-').replace(' ', '_').replace('.','_')
        new_file = ''+ path +  'test'+ module  + now + '.xlsm'
        print(new_file)
        print(f'Файл доступен по адресу {new_file}') 
        wb.SaveAs(new_file)
        wb.Close()
        text.logs = f'По книге {book} - сохранен файл {new_file}\\n\\n'
        if EAP.flag:
            text.logs = 'Открыта система EAP, скрипт завершен'
            sys.exit(0)
def function_books():

Подключение к Excel

def function_for_fi():
    Excel = win32com.client.Dispatch(\"Excel.Application\")
    Excel.Visible = True 
    try:
        path = '\\\\\\SOME_FILE_SERVER\\\\Folders\\\\QA_GROUP\\\\Modules\\\\'
        FI_AA = 'FI_AA\\\\FI_AA_ver10.xlsm'
        PS = 'PS\\\\PS_ver13_stab.xlsm'

        sheets_order_launch_fi = ['AS01','AB01_1','AB01_2', 'AB08_Storno', 'ABUMN_1', 'ABUMN_2', 'ABT1N', 'ZOPROS', 'J3RFDEPRBONUS', 'AFAB', 'ZFIAA_SELL_3', 'ZFIAA_SELL_VGO', 'ZFIAA_DISM_3', 'F92' ]
        sheets_order_launch_ps = ['CJ20N','RDB','Orders', 'ZFDALLLOAD', 'ZPS_STATUS', 'ZOPROS', 'ZRASPRED', 'ZPS_STATUS_GEKS', 'ZOPROS_Itog', 'Storno']

На каждом листе в Excel есть подробная входная и выходная информация, при этом входную информацию можно корректировать. Большая часть листов связана между собой, чтобы можно было провести всю цепочку на одних данных, а последующие шаги не зависели от дополнительных действий пользователя. 

Все скриншоты, которые создаются в процессе регресса, генерируются вместе с документами и проводками. Лишние скриншоты можно удалить прямо на странице в Excel. При необходимости сотрудник может по номеру документа найти нужную проводку или операцию в SAP. Это является прозрачным и удобным способом анализа логов тестирования.

Рядом с каждым шагом в файле появляется текстовое описание, статус «успешно» или «не успешно» пройден шаг и цветовой индикатор — зеленый означает успешно пройденный этап, красный сигнализирует об ошибках. 

Если ошибка является блокирующей для системы и дальнейшее прохождение шагов невозможно, то скрипт остановится, выдаст информационное сообщение и сохранит изменения в файл. Если ошибка не влияет на последующие шаги, то скрипт продолжит работу, а в конце выдаст лог в Excel с отображением корректных и некорректных шагов. При таком раскладе у нас появляется возможность увидеть проблему в моменте и исправить её. 

Также, завершение работы скрипта сопровождается звуковым оповещением.

Дополнительно мы настроили автоматическое удаление листов из общей папки через три дня после их создания.

Что в итоге 

Мы посчитали, сколько рабочего времени ручных тестировщиков мы экономим при использовании инструмента автоматизации. Получилось, что на один кейс при использовании SAP Scripting мы тратим 31 секунду против 148 секунд при ручном тестировании. Таким образом, 80% времени инженеров высвободилось на другие задачи, и мы смогли повысить эффективность тестирования. 

Данный вариант автоматизации является гибким к изменениям. В случае переезда на другую финансовую систему мы перенаправим нашего робота на Python на вызов нужной нам программы. Сейчас одна из наших основных задач – обеспечить качество работы текущего функционала и уже на этой надёжной основе реализовывать улучшения и внедрять новые фичи. Для нашей команды автоматизация тестирования SAP ERP стала интересным и полезным опытом, а бизнесу предоставила доступную, понятную и безотказную систему проверки рабочих процессов.

Комментарии (7)


  1. IvanSTV
    05.06.2024 10:52
    +3

    ой вэй, почесать левое ухо правой ногой. Зачем нужен Питон, если он просто открывает экселевский файл? Можно просто дернуть SAP GUI средствами VBA из экселевского файла, это гораздо проще.


    1. avf48
      05.06.2024 10:52
      +1

      Бюджет освоить... это же SAP))


      1. IvanSTV
        05.06.2024 10:52
        +4

        За офисную малую автоматизацию обычно платят улыбкой. Скорей, это симптом такого явления, как "питонодрочерство", то есть, стремления средствами питона делать все, в том числе и то, что удобней и проще делать другими средствами. Я в свое время рассматривал возможность дергать SAP GUI питоном (была мысль, что можно его статистические библиотеки использовать, а не самому на VBA велосипед писать), но посмотрев, насколько усложняется реализация, масштабирование, развертывание для энного количества пользователей, поддержка, просто написал функционал на VBA - он есть на каждом рабочем месте, надо просто макросы разрешить.


        1. economist75
          05.06.2024 10:52

          Да разрешен VBA уже везде и всюду, но статлибы на нем не родились почему-то ни у кого. И это хорошо, потому либы на Python уж слишком хорошие, быстрые. А объем уже решенных python-вопросов в Сети и AI-дополнялках недостижим не только на VBA, но и на всех других языках. Так что "стремления" нет, люди просто используют самый удобный бесплатный инструмент.
          Насчет трудностей развертывания - их нет. Сейчас трудно представить себе офисный ПК без OpenOffice|LibreOffice. Внутри внезапно уже есть Python... Прав админа для установки пакетов не нужны, через GP их установку запретить крайне сложно (и хотелось бы взглянуть на этих сказочных ***, которые в наше время не помогают, запрещают людям работать эффективно).


          1. IvanSTV
            05.06.2024 10:52

            Да разрешен VBA уже везде и всюду, но статлибы на нем не родились почему-то ни у кого.

            потому что VBA пользуется в основном малоквалифицированным пользователем, и логично, что на нем библиотеки разрабатывают "не только лишь все". Я находил библиотеки для VBA, но нужных мне функций не было.

            А объем уже решенных python-вопросов в Сети 

            угу-угу, особенно для работы с экселькой :) В большинстве случаев, которые я видел на обработках экселевских файлов питоном, VBA вполне справлялся и без него, и в целом и код, и подход был гораздо проще.

            Сейчас трудно представить себе офисный ПК без OpenOffice|LibreOffice.

            какая бедная фантазия

            Насчет трудностей развертывания - их нет.

            Угу, но есть трудности использования невкалифицированным пользователем. И через питон многие простые вещи делать ИЗЛИШНЕ. Ваш КО. Я плохо представляю, как я буду операторов, которые по букве инструкции копипастят и жмут на кнопки для распечатки, обучать устанавливать и запускать питоновские скрипты.

            Прав админа для установки пакетов не нужны,

            правда что ли? а у нас админ почему-то по-другому думает


        1. Aizz
          05.06.2024 10:52

          Согласен, что конкретно тут, всё что делает питон - это запускает эксель и жмёт "выполнить макрос". Но в целом, если нужны именно питоновские библиотеки внутри экселя, вполне можно вызывать WScript.shell и скармливать ему написанный код. Да и сам майрософт уже задумался об этом: https://aka.ms/python-in-excel (хоть и только для офис 365 и выполняемый код в облаке)


  1. vis_inet
    05.06.2024 10:52

    Скажите, а сам SAP вы планируете на что-то другое заменять?