Пример теста на Robot Framework с pywinauto

Введение

Перед изучением этой статьи убедитесь, что вы владете материалом, изложенном в статье «Архитектура тестов на Robot Framework»

Пример

Рассмотрим пример тестирования Desktop приложения, которое создаёт базы данных PostgreSQL .

Для работы с PostgreSQL нам понадобится библиотека psycopg2 , для контроля приложения используем PyWinAuto

robot |-- __init__.py |-- libraries | |-- __init__.py | |-- config.py | |-- fixtures.py | |-- pg_utils.py | |-- app.py | `-- utils.py |-- resources | `-- Fixtures.robot |-- tests `-- test_new_db.robot

test_new_db.robot

*** Settings *** Documentation Tests that new database can be created with unique name Resource ../resources/fixtures.robot Library ../libraries/app.py Test Setup Start TestCase Test Teardown Finish TestCase *** Variables *** *** Test Cases *** Impossible To Create Database With Existing Name ${app} = App Tool Click Button ${app} Detect path Click Button ${app} Save Click Button ${app} New ${db_name} = Random Existing Db Name Log ${db_name} Type New Db Name ${app} ${db_name} Click Button ${app} OK ${title_to_verify} = Db Exists Title ${db_name} ${impossible} = Window Title Exists ${app} ${title_to_verify} Should Be True ${impossible}

Fixtures.robot

*** Settings *** Documentation Test Setup and Test Teardown Library ../libraries/fixtures.py *** Keywords *** Start TestCase Setup AppTool Finish TestCase Teardown AppTool

config.py

import os import configparser def credentials(filename="C:\sandbox\pywinauto\database_dos.toml", section="postgresql"): print("config.credentials()") try: os.path.isfile(filename) print(f"file {filename} is found") except FileNotFoundError as e: print(e) # create a parser config = configparser.ConfigParser() # read config file print(config.read(filename)) # get section, default to postgresql db = {} if config.has_section(section): params = config.items(section) for param in params: db[param[0]] = param[1] else: raise Exception(f"Section {section} not found in the {filename} file") return db

fixtures.py

import os import utils # App Tool def setup_AppTool(usecase="default", lang="en"): """ Copies default AppTool.prm file to settings directory Closes opened AppTool instance """ print("AppTool TEST SETUP STARTED") cur_dir = os.getcwd() print(f"Current Directory {cur_dir}") utils.close_software_instance("AppTool") utils.restore_settings("App Tool", usecase, lang) # utils.run_tested_app("AppTool") print("AppTool TEST SETUP FINISHED") def teardown_AppTool(sqlite_db_path=False): print("AppTool TEST TEARDOWN STARTED") utils.close_software_instance("AppTool") if sqlite_db_path: utils.delete_file(sqlite_db_path) print("AppTool TEST TEARDOWN FINISHED")

pg_utils.py

#!/usr/bin/python import psycopg2 from robot.api import logger from config import credentials """ mode = "robot" if mode == "robot": print = log_to_console() def log_to_console(arg): logger.console('Message only to console.') """ def connect(): """ Connect to the PostgreSQL database server """ conn = None try: # read connection parameters params = credentials() for p in params: print(f"param: {p}") # connect to the PostgreSQL server print('Connecting to the PostgreSQL database...') conn = psycopg2.connect(**params) # create a cursor cur = conn.cursor() # execute a statement print('PostgreSQL database version:') cur.execute('SELECT version()') # display the PostgreSQL database server version db_version = cur.fetchone() print(db_version) return cur except (Exception, psycopg2.DatabaseError) as error: print(f"pg_utils.connect() error: {error}") def get_list_of_databases(): print("get_list_of_databases()") cur = connect() cur.execute("SELECT datname FROM pg_catalog.pg_database") databases = cur.fetchall() db_list = [db[0] for db in databases] cur.close() return db_list

app.py

import random import time from pywinauto.application import Application import utils import pg_utils as pg def db_exists_title(db_name: str) -> str: return f"Database {db_name} already exists!" def app_tool() -> object: app = Application(backend='uia').start(r"C:\Program Files\AredelApp\AppTool.exe") app = Application(backend='uia').connect(title='App Tool', timeout=100) return app def click_button(app: str, button: str): # print(f"app: {app}") # print(f"button: {button}") match button: case "Detect path": detect_path = app.AppTool.child_window( title="Detect path", control_type="Text" ).wrapper_object() detect_path.click_input() case "Save": save = app.AppTool.child_window( title="Save", control_type="Text" ).wrapper_object() save.click_input() case "New": if app.AppTool.NewButton.child_window( title="New", control_type="Text" ).exists(timeout=10): # time.sleep(1) new = app.AppTool.NewButton.child_window( title="New", control_type="Text" ).wrapper_object() print(f"'New' button is visible: {new.is_visible()}") # time.sleep(1) new.click_input() case "OK": ok = app.AppTool.Newdatabase.child_window( title="OK", control_type="Text" ).wrapper_object() ok.click_input() case "Close": close = app.AppTool.child_window(title="Close", control_type="Text") close.click_input() def type_new_db_name(app, db_name: str): if app.AppTool.Newdatabase.child_window( title="Name:", control_type="Text" ).exists(timeout=10): new_database = app.AppTool.Newdatabase.child_window( title="Name:", control_type="Text" ).wrapper_object() # new_database = app.AppTool.child_window(title="Name:", control_type="Text").wrapper_object() print(f"Tries to enter new db name: {db_name}") new_database.type_keys(db_name) def window_title_exists(app, title_to_check) -> bool: title_exists = app.AppTool.child_window( title=title_to_check, control_type="Text" ).exists(timeout=5) return title_exists def random_existing_db_name() -> str: print("random_existing_db_name()") existing_dbs = pg.get_list_of_databases() print(existing_dbs) i = random.randrange(len(existing_dbs)) db = existing_dbs[i] print(f"Database {db} was randomly choosen out of existing databases") print("/random_existing_db_name()") return db

utils.py

import os import uuid import shutil import psutil def close_software_instance(app_name: str): print(f"close_software_instance(): {app_name}") if app_name in ["TB", "Top Bicycle", "TopBicycle"]: app_name = "TopBicycle.exe" elif app_name in ["AT", "Aredel Tool", "AredelTool"]: app_name = "AredelTool.exe" else: print(f"close_software_instance(): Software is not from the known app list\ using it's name as it is: {app_name:-^140}") for proc in psutil.process_iter(['pid', 'name']): if proc.info["name"] == app_name: print(proc.info["pid"]) p = psutil.Process(proc.pid) p.terminate() print(f"/close_software_instance(): {app_name}") def delete_file(file: str): print(f"delete_file({file})") if (os.path.exists(file)): os.remove(file) print(f"File: {file} is deleted.") else: print(f"File: {file} does not exist.") print("/delete_file()") def delete_dir_content(path: str): for file in os.listdir(path): file_path = os.path.join(path, file) try: if os.path.isfile(file_path) or os.path.islink(file_path): os.unlink(file_path) elif os.path.isdir(file_path): shutil.rmtree(file_path) except Exception as e: print(f"Failed to delete {file_path}. Reason: {e}") def restore_settings(software: str, usecase="default", lang="en"): print(f"restore_settings() for {software:-^140}") rs_base = "C:\ProgramData\Aredel\AppPackage\" prm_base = "C:\AutoTests\pywinauto\prm_files\" if software in ["TP", "Top Bicycle", "TopBicycle"]: source_prm = prm_base + "result_viewer\" + usecase + "\" + lang + "\TopBicycle.prm" target_path = rs_base + "TopBicycle.prm" elif software in ["AT", "Aredel Tool", "AredelTool"]: print("AredelTool uses different setting system - not .prm files") current_user = os.getlogin() print(f"current user is {current_user}") settings_path = "C:\Users\" + current_user + "\AppData\Local\AredelTool" delete_dir_content(settings_path) source_prm = prm_base + "aredel_tool\default\en\Foo.prm" target_path = prm_base + "aredel_tool\default\en\Bar.prm" else: print(f"utils.restore_settings(): Software is not specified\ properly to restore settings for usecase: {usecase:-^140}") print(f"Rewriting {target_path} with {source_prm:-^140}") shutil.copyfile(source_prm, target_path) print(f"\restore_settings() for {software:-^140}")

Автор статьи: Андрей Олегович

Похожие статьи
Robot Framework
Основы
Архитектура
RFBrowser
Переменные: set, reassign…
if else: Условные операторы
in: Оператор принадлежности к списку
Циклы
Перенос на новую строку
[Tags]: теги
Collections: списки, словари
JSON
Передача аргументов в кейворд
Evaluate
Поддержка Robot в PyCharm
Ошибки

Поиск по сайту

Подпишитесь на Telegram канал @aofeed чтобы следить за выходом новых статей и обновлением старых

Перейти на канал

@aofeed

Задать вопрос в Телеграм-группе

@aofeedchat

Контакты и сотрудничество:
Рекомендую наш хостинг beget.ru
Пишите на info@urn.su если Вы:
1. Хотите написать статью для нашего сайта или перевести статью на свой родной язык.
2. Хотите разместить на сайте рекламу, подходящую по тематике.
3. Реклама на моём сайте имеет максимальный уровень цензуры. Если Вы увидели рекламный блок недопустимый для просмотра детьми школьного возраста, вызывающий шок или вводящий в заблуждение - пожалуйста свяжитесь с нами по электронной почте
4. Нашли на сайте ошибку, неточности, баг и т.д. ... .......
5. Статьи можно расшарить в соцсетях, нажав на иконку сети: