python-selenium-pytest-allure UI自动化测试

2023-11-07

一、下载及安装插件

python

selenium

pytest

allure

辅助:pyautoit  pymysql  

二、代码框架

1、基础文件

[main.py]

if __name__ == '__main__':
    pytest.main(["--alluredir", "./alluretmp"])
    # pytest.main()
    os.system("allure generate ./alluretmp -o ./allure-report --clean")

[pytest.ini]

[pytest]

addopts = -vs -rerun=2
;addopts = -vs
testpaths = ./testcase
python_files = test*.py
python_classes =Test*
python_functions = test_*
markers = smoke
    story: sdv
    sit: sit
    uat: uat

[conftest.py]

import time

import allure
import pytest
import os
from selenium import webdriver
from common.path import Path
from common.getFileDatas import GetFileDatas
from page.login.loginpage import LoginPage

driver = None


@pytest.fixture(scope='session', autouse=True)
def clean_tmp_allure():
    tmp_allure_path = Path().get_project_path() + r'\alluretmp'
    tmpallure_files = os.listdir(tmp_allure_path)
    for fl in tmpallure_files:
        fl_path = tmp_allure_path + "\\" + fl
        if fl.endswith(".json") or fl.endswith(".txt"):
            os.remove(fl_path)


@pytest.fixture(scope='class')
def start_login():
    global driver
    # if driver is None:
    #     driver = webdriver.Chrome()
    driver = webdriver.Chrome()
    index_url = GetFileDatas().get_ini_data_bypath_section(Path().get_project_path() + r'\config\env.ini', 'test_env')[
        'testurl']
    login_url = '/user/login'
    LoginPage(driver).login(index_url + login_url,
                            GetFileDatas().get_ini_data_bypath_section(Path().get_project_path() + r'\config\env.ini',
                                                                       'user_info')[
                                'username'],
                            GetFileDatas().get_ini_data_bypath_section(Path().get_project_path() + r'\config\env.ini',
                                                                       'user_info')[
                                'pwd'])
    yield driver
    driver.quit()


#  调试无头模式,还未ok
# @pytest.fixture(scope='class')
# def start_driver():
#     """是否启用headless"""
#     chrome_option = Options()
#     if GetFileDatas().get_ini_data_bypath_section(Path().get_project_path() + r'\config\env.ini', 'selenium')[
#         'headless']:
#         chrome_option.add_argument('--headless')
#         chrome_option.add_argument('--disable-software-rasterizer')
#     driver = webdriver.ChromeOptions(options=chrome_option)
#     yield driver
#     driver.quit()


""" 控制台用例名称乱码"""
def pytest_collection_modifyitems(items):
    print("收集到的所有测试用例{}".format(items))
    for item in items:
        item.name = item.name.encode("utf-8").decode("unicode_escape")
        item._nodeid = item.nodeid.encode("utf-8").decode("unicode_escape")


@pytest.hookimpl(tryfirst=True, hookwrapper=True)
def pytest_runtest_makereport(item, call):
    """
    获取每个用例的钩子函数
    :param item:
    :param call:
    :return:
    """
    outcome = yield
    rep = outcome.get_result()
    # 以下为实现异常截图的代码:
    # rep.when可选参数有call、setup、teardown,
    # call表示为用例执行环节、setup、teardown为环境初始化和清理环节
    # 这里只针对用例执行且失败的用例进行异常截图
    if rep.when == "call" and rep.failed:
        mode = "a" if os.path.exists("failures") else "w"
        with open("failures", mode) as f:
            if "tmpdir" in item.fixturenames:
                extra = " (%s) " % item.funcargs["tmpdir"]
            else:
                extra = ""
            f.write(rep.nodeid + extra + "\n")
        item.name = item.name.encode("utf-8").decode("unicode-escape")

        file_name = '{}.png'.format(str(round(time.time() * 1000)))
        path = os.path.join(Path().get_project_path()+r'\alluretmp', file_name)

        driver.save_screenshot(path)

        if hasattr(driver, "get_screenshot_as_png"):
            with allure.step("添加失败截图"):
                # get_screenshot_as_png实现截图并生成二进制数据
                # allure.attach直接将截图二进制数据附加到allure报告中
                allure.attach(driver.get_screenshot_as_png(), "失败截图", allure.attachment_type.PNG)


if __name__ == '__main__':
    pass

[common/basepage.py]

import time
import autoit
from selenium.webdriver import Keys
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.remote.webdriver import WebDriver
from selenium.webdriver.support.select import Select


class BasePage:

    def __init__(self, driver):
        self.driver = driver

    def visit(self, url):
        self.driver.get(url)
        self.driver.maximize_window()
        time.sleep(1)

    def locator(self, loc):
        return WebDriverWait(self.driver, 10).until(EC.visibility_of_element_located(loc))

    def get_ele_isvisibility(self, loc):
        return self.locator(loc).is_displayed()

    def get_ele_isexist(self, loc):
        try:
            self.locator(loc)
            return True
        except:
            return False

    def input(self, loc, txt):
        self.locator(loc).send_keys(txt)

    """"清空textarea文本域的内容"""

    def clear(self, loc):
        self.locator(loc).clear()

    """通过键盘事件,清空input输入框的内容"""

    def clear_input(self, loc):
        self.locator(loc).send_keys(Keys.CONTROL, 'A')
        self.locator(loc).send_keys(Keys.DELETE)

    """ 针对下拉选择框定位,引用select类"""
    def select_by_value(self,loc,value):
        Select(self.locator(loc)).select_by_value(value)

    @staticmethod
    def execute_script(script1):
        WebDriver().execute_script(script=script1)

    def click(self, loc):
        self.locator(loc).click()

    def wait(self, time_):
        time.sleep(time_)

    def quit(self):
        self.driver.quit()

    def close_return_Home(self):
        self.driver.close()
        home_page = self.driver.window_handles[0]
        self.driver.switch_to.window(home_page)

    def get_ele_attr_value(self, loc, name):
        ele = self.locator(loc)
        return ele.get_attribute(name)

    """ 鼠标悬停事件"""

    def move_to_element(self, loc):
        ActionChains(self.driver).move_to_element(self.locator(loc)).perform()

    """ 获取页面元素的内容"""

    def get_page_ele_txt(self, loc):
        return self.locator(loc).text

    """校验某一个button按钮是否disable"""

    def get_ele_isdisable(self, loc):
        return self.locator(loc).is_enabled()

    """获取表格中某一单元格的值
    """

    def get_table_cell_value(self, loc):
        return self.locator(loc).text

    def click_table_cell_btn(self, loc):
        self.locator(loc).click()

    """ 获取表格中某一单元格的值"""

    def check_table_cell_value(self, loc, row, col):
        cell_loc = (loc[0], loc[1] + "/tr[" + str(row) + "]" + "/td[" + str(col) + "]")
        return self.locator(cell_loc).text

    """判断某元素是否置灰"""

    def check_element_isenable(self, loc):
        return self.locator(loc).is_enabled

    """alert弹框,只有一个确定按钮
    1、点击确定
    2、获取alert弹框的内容"""

    def switch_to_alert_comfirm(self):
        time.sleep(1)
        self.driver.switch_to_alert.accept()

    def swithch_to_alert_dismiss(self):
        time.sleep(1)
        self.driver.switch_to_alert.dismiss()

    def get_switch_to_alert_text(self):
        time.sleep(2)
        return self.driver.switch_to.alert.text

    def switch_to_window(self):
        return self.driver.switch_to.window('打开')

    """鼠标当前位置偏移量"""

    def mouse_move_to_ele(self, loc, xoffset, yoffset):
        ActionChains(self.driver).move_to_element(self.locator(loc)).move_by_offset(xoffset,
                                                                                    yoffset).release().click().perform()

    """鼠标拖动"""

    def mouse_drag_and_drop_ele(self, source_ele, target_ele):
        ActionChains(self.driver).drag_and_drop(self.locator(source_ele), self.locator(target_ele))

    """ 切换到新的window窗口"""

    def swith_to_new_window(self):
        new_window = self.driver.window_handles[-1]
        self.driver.switch_to.window(new_window)
        # new_window = WebDriver().window_handles[-1]
        # WebDriver().switch_to.window(new_window)

    """ 使用autoit处理window系统框
         STEP1: 获取输入框焦点,输入文件url
         STEP2: 点击打开文件,并再次输入文件名
         STEP3: 点击打开,弹出框小时"""

    @staticmethod
    def select_window_alert_files(file_path, files):
        time.sleep(1)
        autoit.control_focus("打开", "[CLASS:Edit; INSTANCE:1]")
        autoit.control_set_text("打开", "[CLASS:Edit; INSTANCE:1]", file_path)
        time.sleep(2)
        autoit.control_click("打开", "[Class:Button; instance:1]")
        time.sleep(2)
        autoit.control_set_text("打开", "[CLASS:Edit; INSTANCE:1]", files)
        time.sleep(2)
        autoit.control_click("打开", "[Class:Button; instance:1]")
        time.sleep(2)

        """还有针对alert弹框点击确认,取消操作的方法封装
               还有切换iframe
               返回到主窗口
           """

[common/const.py]

from common.getFileDatas import GetFileDatas
from common.path import Path


class Const:
    def __init__(self):
        self.user_info = GetFileDatas().get_ini_data_bypath_section(Path().get_project_path() + r'\config\env.ini', 'user_info')
        self.base_url = GetFileDatas().get_ini_data_bypath_section(Path().get_project_path() + r'\config\env.ini', 'test_env')['testurl']
        self.base_test_datas_path = GetFileDatas().get_ini_data_bypath_section(Path().get_project_path() + r'\config\env.ini', 'test_data_path')['path']
        self.mysql_info = GetFileDatas().get_ini_data_bypath_section(Path().get_project_path() + r'\config\env.ini','mysql')
        self.normal_role = GetFileDatas().get_ini_data_bypath_section(Path().get_project_path() + r'\config\env.ini','role_rule')['normal_role']
        self.admin_role = GetFileDatas().get_ini_data_bypath_section(Path().get_project_path() + r'\config\env.ini', 'role_rule')['admin_role']

[common/dbconn.py]

import pymysql


class Dbconn(object):
    def __init__(self, dbinfo):
        self.db = pymysql.connect(cursorclass=pymysql.cursors.DictCursor, **dbinfo)
        self.cursor = self.db.cursor()

    def query_data(self, sql):
        try:
            self.cursor.execute(sql)
            return self.cursor.fetchall()
        except Exception as e:
            print(e)

    def execute_sql(self,sql):
        try:
            self.cursor.execute(sql)
            self.db.commit()
        except Exception as e:
            print(e)

    def close(self):
        self.cursor.close()
        self.db.close()

[common/getFileDatas.py]
import yaml
import configparser


class GetFileDatas:
    @staticmethod
    def get_yml_data_bypath(path):
        try:
            with open(path, 'r', encoding='utf-8') as f:
                data = yaml.load(stream=f, Loader=yaml.FullLoader)
                return data
        except Exception as e:
            print('get yml data failure,reason is :{}'.format(e))

    @staticmethod
    def get_ini_data_bypath_section(path, section):
        try:
            _configparser = configparser.ConfigParser()
            _configparser.read(path, encoding='utf-8')
            options = _configparser.options(section)
            value_lst = {}
            for option in options:
                value = _configparser.get(section, option)
                if option not in value_lst:
                    value_lst[option] = value
            return value_lst

        except Exception as e:
            print('get ini data failure,reason is:{}'.format(e))


if __name__ == '__main__':
    test_get_data = GetFileDatas()
    # print(test_get_data.get_yml_data_bypath(r'E:\py\test\testdata\loginYml.yml'))
    print(test_get_data.get_ini_data_bypath_section(r'env.ini', 'mysql'))

[common/logger.py]

import logging
import os.path

from common.path import Path


class Logger:
    def __init__(self,name):
        self.logger = logging.getLogger(name)
        self.logger.setLevel(logging.DEBUG)
        self.project_path = Path().get_project_path()

    def get_logger(self):
        hander = logging.FileHandler(filename=self.project_path+'/logs/log.log',encoding='utf-8')
        hander.setLevel(logging.DEBUG)
        formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
        hander.setFormatter(formatter)
        self.logger.addHandler(hander)
        return self.logger

[common/path.py]

import os


class Path:
    @staticmethod
    def get_project_path():
        return os.path.dirname(os.path.abspath(os.path.dirname(__file__)))


if __name__ == '__main__':
    print(Path().get_project_path())

[common/update_db_data.py]

import time

from common.const import Const
from common.dbconn import Dbconn
from common.getFileDatas import GetFileDatas


class Update_Db_Data:
    @staticmethod
    def get_my_sql_info():
        mysql_info = Const().mysql_info
        mysql_info["port"] = int(mysql_info["port"])
        return mysql_info

    def update_status(self, patient_name, study_status, study_realgin_status, series_dx_status):
                  5
           """
        try:
            update_study_sql = f'update t_study ts set ts.status ={study_status},ts.update_dt = SYSDATE() where exists \
                (select 1 from t_patient tt where tt.id= ts.patient and tt.patient_name =\'{patient_name}\')'
            update_study_realign_sql = f'update t_study_realign ts set ts.request_status ={study_realgin_status},ts.update_dt = ' \
                                       f'SYSDATE() where exists (select 1 from t_patient tt,t_study tst where tt.id= ' \
                                       f'tst.pati' \
                                       f'ent and tst.id = ts.study and tt.patient_name =\'{patient_name}\') '
            update_series_sql = f'update t_series_dx tsd set tsd.request_status = {series_dx_status}, tsd.request_dt  ' \
                                f'= SYSDATE()  where EXISTS( select 1 from t_patient tp where tsd.patient = tp.id and ' \
                                f'tp.patient_name = \'{patient_name}\') '
            db = Dbconn(self.get_my_sql_info())
            db.execute_sql(update_study_sql)
            db.execute_sql(update_study_realign_sql)
            if series_dx_status != '':
                db.execute_sql(update_series_sql)
            db.close()
            time.sleep(1)
        except Exception as e:
            print(e)

    def delete_user_account(self, username):
       
        delete_user_data_sql1 = f'delete from t_user_role where user IN(select id from t_user tu where tu.username = \'{username}\' )'
        delete_user_data_sql2 = f'delete from t_user  where username =\'{username}\''
        db = Dbconn(self.get_my_sql_info())
        db.execute_sql(delete_user_data_sql1)
        db.execute_sql(delete_user_data_sql2)
        db.close()

[config/env.ini]

[test_env]
testurl = 

[user_info]
username = 
pwd = 

[mysql]
host = 
port = 
user = 
password =  
database = 


[test_data_path]
path  = E:\autotest\ais_ui_testdatas

[selenium]
headless = True
thread_num = 1

[role_rule]
normal_role = 
admin_role = 

[page/login/loginpage.py]

import time

import allure
from selenium.webdriver.common.by import By
from common.basepage import BasePage
from selenium import webdriver

from common.logger import Logger


class LoginPage(BasePage):
    logger = Logger('LoginPage').get_logger()
    username = (By.ID, "username")
    password = (By.ID, "password")
    login_btn = (By.XPATH, '//*[@id="root"]/div/div[3]/div[1]/div[2]/form/button')
    """登录成功/失败后的页面告警page元素"""
    login_success_page = (By.XPATH, '/html/body/div[2]/div/div/div/div/div')
    login_fail_page = (By.XPATH, '/html/body/div[2]/div/div/div/div/div/div[1]')
    username_error_msg = (By.XPATH, '//*[@id="root"]/div/div[3]/div[1]/div[2]/form/div[2]/div/div[2]/div')
    password_error_msg = (By.XPATH, '//*[@id="root"]/div/div[3]/div[1]/div[2]/form/div[3]/div/div[2]/div')
    """icon"""
    username_close_circle = (By.XPATH, '//*[@id="root"]/div/div[3]/div[1]/div[2]/form/div[2]/div/div/div/span/span['
                                       '2]/span')
    password_eye = (By.XPATH, '//*[@id="root"]/div/div[3]/div[1]/div[2]/form/div[3]/div/div/div/span/span[2]/span')

    """退出登录模块"""
    account_name_ele = (By.XPATH, '//*[@id="root"]/div/section/div[2]/header[2]/div/div[3]/div[2]/span/span[2]')
    login_out_btn = (By.XPATH, '/html/body/div[3]/div/div/ul/li[3]/span')

    def login(self, url, username, pwd):
        self.logger.debug("进入登录页面:{}".format(url))
        with allure.step("进入登录页面:{}".format(url)):
            time.sleep(1)
            self.visit(url)
        self.logger.debug("输入用户名:{}  密码:{}".format(username if username else '为空', pwd if pwd else '为空'))
        with allure.step("输入用户名:{}  密码:{}".format(username if username else '为空', pwd if pwd else '为空')):
            time.sleep(1)
            self.input(self.username, username)
            self.input(self.password, pwd)
        self.logger.debug("点击登录按钮")
        with allure.step("点击登录按钮"):
            time.sleep(1)
            self.click(self.login_btn)
            time.sleep(1)

    def login_out(self):
        with allure.step("鼠标悬停至软件右上角用户真实姓名处"):
            time.sleep(2)
            self.move_to_element(self.account_name_ele)
            time.sleep(1)
        with allure.step("点击退出登录按钮"):
            self.click(self.login_out_btn)

    def check_login_out_issuccess(self,expected_result):
        assert expected_result == self.get_ele_isvisibility(self.login_btn)

    def get_page_msg(self, loc):
        return self.get_page_ele_txt(loc)

    def check_page_msg(self, loc, exep_txt):
        actual_page_msg = self.get_page_ele_txt(loc)
        assert actual_page_msg == exep_txt

    def check_sucesspage_msg(self, exep_txt):
        assert self.get_page_msg(LoginPage.login_success_page) == exep_txt

    def check_failurepage_msg(self, exep_txt):
        assert self.get_page_ele_txt(LoginPage.login_fail_page) == exep_txt

    def check_username_error(self, exep_txt):
        assert self.get_page_ele_txt(LoginPage.username_error_msg) == exep_txt

    def check_password_error(self, exep_txt):
        assert self.get_page_ele_txt(LoginPage.password_error_msg) == exep_txt

[testcase/login/testlogin.py]

import allure
import pytest
from selenium import webdriver

from common.const import Const
from common.logger import Logger
from common.path import Path
from page.login.loginpage import LoginPage
from testdata.login import loginpage_datas


@allure.parent_suite("系统登录页面")
@allure.epic("系统登录")
class TestLoginPage:
    test_url = Const().base_url
    logger = Logger("TestLoginPage").get_logger()

    def setup(self):
        self.driver = webdriver.Chrome()
        self.lp = LoginPage(self.driver)

    def teardown(self):
        self.driver.quit()

    @allure.feature("系统登录")
    @allure.story("登录成功场景")
    @pytest.mark.parametrize('data', loginpage_datas.login_sucess,
                             ids=[i['case_name'] for i in loginpage_datas.login_sucess])
    def test_login_success(self, data):
        username, pwd = data['username'], data['password']
        expected_results = data['expected_results']
        url = self.test_url + '/user/login'
        self.lp.login(url, username, pwd)
        self.logger.debug('校验实际结果是否为:{}'.format(expected_results))
        with allure.step('校验实际结果是否为:{}'.format(expected_results)):
            self.lp.check_sucesspage_msg(expected_results)

    @allure.feature("系统登录")
    @allure.story("登录失败场景")
    @pytest.mark.parametrize('data', loginpage_datas.login_failure,
                             ids=[i['case_name'] for i in loginpage_datas.login_failure])
    def test_login_failure(self, data):
        username, pwd = data['username'], data['password']
        expected_results = data['expected_results']
        url = self.test_url + '/user/login'
        self.lp.login(url, username, pwd)
        self.logger.debug('校验实际结果是否为:{}'.format(expected_results))
        with allure.step('校验实际结果是否为:{}'.format(expected_results)):
            self.lp.check_failurepage_msg(expected_results)

    @allure.feature("系统登录")
    @allure.story("登录异常场景")
    @pytest.mark.parametrize('data', loginpage_datas.login_exception,
                             ids=[i['case_name'] for i in loginpage_datas.login_exception])
    def test_login_exception(self, data):
        self.logger.debug("........debug log testing 1.....")
        username, pwd = data['username'], data['password']
        expected_results = data['expected_results']
        url = self.test_url + '/user/login'
        self.lp.login(url, username, pwd)
        self.logger.debug("........debug log testing 2.....")
        self.logger.debug('校验实际结果是否为:{}'.format(expected_results))
        with allure.step('校验实际结果是否为:{}'.format(expected_results)):
            try:
                if data['username'] == '':
                    self.lp.check_username_error(expected_results)
                else:
                    self.lp.check_password_error(expected_results)
            except:
                self.logger.error("........error log testing .....")

    @allure.feature("系统退出")
    @allure.story("登录退出")
    @pytest.mark.parametrize('data', loginpage_datas.login_out,
                             ids=[i['case_name'] for i in loginpage_datas.login_out])
    def test_login_out(self, data):
        username, pwd = data['username'], data['password']
        expected_results = data['expected_results']
        url = self.test_url + '/user/login'
        self.lp.login(url, username, pwd)
        self.lp.login_out()
        with allure.step('校验实际结果是否为'.format(expected_results)):
            self.lp.check_login_out_issuccess(expected_results)


if __name__ == '__main__':
    pytest.main()

[testcase/login/study.py]

import time
import allure
import pytest
from common.const import Const
from common.logger import Logger
from common.update_db_data import Update_Db_Data
from page.ctp.ctppage import CtpPage
from page.studyupload.studyuploadpage import StudyUploadPage
from testdata.studylist import studylistpage_align_datas as slap, studylistpage_datas as slp, \
    studylistpage_delete_datas as delp
from page.studylist.studylistpage import Studylist


@allure.parent_suite("影像列表")
@allure.epic("影像列表")
@pytest.mark.usefixtures("start_login")
class Teststudylist:
    base_url, base_test_datas_path = Const().base_url, Const().base_test_datas_path
    logger = Logger('Teststudylist').get_logger()

    @allure.feature("列表查询")
    @allure.story("正向查询")
    @pytest.mark.parametrize('data', slp.search_conditions_ok, ids=[(i['case_name']) for i in slp.search_conditions_ok])
    def test_study_search_Ok(self, data, start_login):
        with allure.step("初始化影像列表页面:{}".format(Teststudylist.base_url + data['page_url'])):
            study_lst_page = Teststudylist.base_url + data['page_url']
        with allure.step("获取测试用例数据:{}".format(data['case_name'])):
            search_data_tmp = data['search_conditions']
            search_datas = []
            for i in search_data_tmp:
                search_datas.append(search_data_tmp[i])
            expected_results = data['expected_results']
        with allure.step("进入影像列表页面{}".format(study_lst_page)):
            sl = Studylist(start_login)
            sl.visit(study_lst_page)
        with allure.step("查询影像"):
            sl.search(search_datas)
        with allure.step('校验CTP列表患者姓名是否等于{},列表列表总记录是否等于{}'.format(data['expected_results']['patient_name'],data['expected_results']['result_count'])):
            sl.check_study_list_count(expected_results['result_count'])
            sl.check_study_list_cell_value(1, 3, expected_results['patient_name'])

    @allure.feature("列表查询")
    @allure.story("反向查询")
    @pytest.mark.parametrize('data', slp.search_conditions_nodata,
                             ids=[(i['case_name']) for i in slp.search_conditions_nodata])
    def test_study_search_nodata(self, data, start_login):
        with allure.step("初始化影像列表页面:{}".format(Teststudylist.base_url + data['page_url'])):
            study_lst_page = Teststudylist.base_url + data['page_url']
        with allure.step("获取测试用例数据:{}".format(data['case_name'])):
            search_data_tmp = data['search_conditions']
            search_datas = []
            for i in search_data_tmp:
                search_datas.append(search_data_tmp[i])
            expected_results = data['expected_results']
        with allure.step("进入影像列表页面{}".format(study_lst_page)):
            sl = Studylist(start_login)
            sl.visit(study_lst_page)
        with allure.step("查询影像"):
            sl.search(search_datas)
        with allure.step("校验影像列表是否展示为{}".format(data['expected_results'])):
            sl.check_study_list_nodata_cell(expected_results)

    @allure.feature("影像校正")
    @allure.story("校正成功场景")
    @pytest.mark.parametrize('data', slap.align_ok, ids=[(i['case_name']) for i in slap.align_ok])
    def test_study_align_success(self, data, start_login):
        try:
            with allure.step("初始化影像列表页面:{}".format(Teststudylist.base_url + data['page_url'])):
                study_lst_page = Teststudylist.base_url + data['page_url']
            with allure.step("获取测试用例数据:{}".format(data['case_name'])):
                expected_results = data['expected_results']
                search_data_tmp = data['search_conditions']
                search_datas = []
                for i in search_data_tmp:
                    search_datas.append(search_data_tmp[i])
            with allure.step("进入影像列表页面{}".format(study_lst_page)):
                sl = Studylist(start_login)
                sl.visit(study_lst_page)
            with allure.step("查询待校正的影像"):
                sl.search(search_datas)
            with allure.step("点击校正"):
                sl.click_study_list_align_btn(1, 13)
                time.sleep(1)
            with allure.step("校验实际结果是否为已校正"):
                time.sleep(180)
                while sl.get_study_list_cell_value(1, 11) in ['校正中', '校正异常']:
                    time.sleep(10)
                assert sl.get_study_list_cell_value(1, 11) == expected_results
        except Exception as e:
            self.logger.error(e)
        finally:
            with allure.step("恢复测试数据状态为{}".format(data['status'])):
                Update_Db_Data().update_status(data['search_conditions']['patientname'], 0,0,'') if data['status'] in [
                    '未校正'] else Update_Db_Data().update_status(data['search_conditions']['patientname'], 2, 2,'') if \
                data['status'] in ['已校正'] else 0

    @allure.feature("影像校正")
    @allure.story("不可校正场景")
    @pytest.mark.parametrize('data', slap.align_no, ids=[(i['case_name']) for i in slap.align_no])
    def test_study_align_faiure(self, data, start_login):
        try:
            with allure.step("初始化影像列表页面:{}".format(Teststudylist.base_url + data['page_url'])):
                study_lst_page = Teststudylist.base_url + data['page_url']
            with allure.step("获取测试用例数据:{}".format(data['case_name'])):
                search_data_tmp = data['search_conditions']
                search_datas = []
                for i in search_data_tmp:
                    search_datas.append(search_data_tmp[i])
            with allure.step("修改影像状态为{}".format(data['status'])):
                Update_Db_Data().update_status(data['search_conditions']['patientname'], 1,1,'') if data['status'] in [
                    '校正中'] else Update_Db_Data().update_status(data['search_conditions']['patientname'], 3,2,3) if data[
                                                                                                                           'status'] in [
                                                                                                                           '分析中'] else 0
                time.sleep(1)
            with allure.step("进入影像列表页面{}".format(study_lst_page)):
                sl = Studylist(start_login)
                sl.visit(study_lst_page)
            with allure.step("查询待校正的影像"):
                sl.search(search_datas)
                time.sleep(1)
            with allure.step("校验当前影像所在行的校正按钮是否是否为:{}".format(data['expected_results'])):
                sl.check_study_lst_align_btn_isenable(1, 13, data['expected_results'])
        except Exception as e:
            self.logger.error(e)
        finally:
            with allure.step("恢复测试数据"):
                Update_Db_Data().update_status(data['search_conditions']['patientname'], 2, 2,'') if data['status'] in [
                    '校正中'] else Update_Db_Data().update_status(data['search_conditions']['patientname'], 4, 2,4) if data[
                                                                                                                           'status'] in [
                                                                                                                           '分析中'] else 0

    @allure.feature("影像删除")
    @allure.story("影像删除成功场景")
    @pytest.mark.parametrize('data', delp.delete_success, ids=[(i['case_name']) for i in delp.delete_success])
    def test_study_delete_success(self, data, start_login):
        # driver = webdriver.Chrome()
        # with allure.step("系统登录"):
        #     index = LoginPage(start_login)
        #     index.login(Teststudylist.base_url + data['login_url'], Teststudylist.user_info['username'],
        #                 Teststudylist.user_info['pwd'])
        with allure.step("初始化影像列表页面:{}".format(Teststudylist.base_url + data['page_url'])):
            studypage = Studylist(start_login)
            study_lst_page = Teststudylist.base_url + data['page_url']
        with allure.step("进入影像上传页面,上传影像"):
            upload_page = Teststudylist.base_url + data['upload_url']
            up = StudyUploadPage(start_login)
            up.visit(upload_page)
            print('path:{}'.format(Teststudylist.base_test_datas_path + data['upload_datas']['file_path']))
            up.upload_files_flow(Teststudylist.base_test_datas_path + data['upload_datas']['file_path'],
                                 data['upload_datas']['files_name'])
            time.sleep(1)
        with allure.step("进入影像列表页面,并查询到需要删除的影像"):
            studypage.visit(study_lst_page)
            datas_tmp = data['search_conditions']
            datas = []
            for i in datas_tmp:
                datas.append(datas_tmp[i])
            print('data is {}'.format(datas))
            studypage.search(datas)
            time.sleep(1)
        if data['status'] in ['已分析', '已校正']:
            with allure.step('发起影像校正'):
                studypage.click_study_list_align_btn(1, 13)
                time.sleep(150)
                while not studypage.get_study_list_cell_value(1, 11) in ['已校正', '校正异常']:
                    time.sleep(30)
        if data['status'] in ['已分析']:
            with allure.step('发起影像分析流程'):
                ctp_page = Teststudylist.base_url + data['ctp_url']
                ctp = CtpPage(start_login)
                with allure.step("进入CTP页面,查询待分析影像"):
                    ctp.visit(ctp_page)
                    datas_tmp = data['search_conditions']
                    datas = []
                    for i in datas_tmp:
                        datas.append(datas_tmp[i])
                    ctp.search_flow(datas)
                with allure.step("进入分析详情页选取动静脉点"):
                    ctp.analysis_flow(1, 13)
                time.sleep(240)
                with allure.step("返回至影像列表页、等待分析完成"):
                    studypage.visit(study_lst_page)
                    studypage.search(datas)
                    while studypage.get_study_list_cell_value(1, 11) not in ['分析异常', '已分析']:
                        time.sleep(10)
        with allure.step("开始删除影像"):
            studypage.click_study_list_del_btn(1, 13)
            time.sleep(2)
            studypage.click_study_lst_del_alert_ok_btn()
        with allure.step("校验影像是否删除成功"):
            studypage.click_search_btn()
            studypage.check_study_list_nodata_cell(data['expected_results'])

    @allure.feature("影像删除")
    @allure.story("影像删除失败场景")
    @pytest.mark.parametrize('data', delp.delete_failure, ids=[(i['case_name']) for i in delp.delete_failure])
    def test_study_delete_failure(self, data, start_login):
        try:
            with allure.step("初始化影像列表页面"):
                studypage = Studylist(start_login)
                study_lst_page = Teststudylist.base_url + data['page_url']
            with allure.step("修改影像状态为{}".format(data['status'])):
                Update_Db_Data().update_status(data['search_conditions']['patientname'], 1,1,'') if data['status'] in [
                    '校正中'] else Update_Db_Data().update_status(data['search_conditions']['patientname'], 3,2,3) if data[
                                                                                                                           'status'] in [
                                                                                                                           '分析中'] else 0
                time.sleep(1)
            with allure.step("进入影像列表页面,并查询到需要删除的影像"):
                studypage.visit(study_lst_page)
                datas_tmp = data['search_conditions']
                datas = []
                for i in datas_tmp:
                    datas.append(datas_tmp[i])
                studypage.search(datas)
                time.sleep(1)
            with allure.step("校验实际结果是否等于预期结果({})".format(data['expected_results'])):
                studypage.check_study_lst_del_btn_isenable(1, 13, data['expected_results'])
        except Exception as e:
            self.logger.error(e)
        finally:
            with allure.step("恢复测试数据"):
                Update_Db_Data().update_status(data['search_conditions']['patientname'], 2,2,'') if data['status'] in [
                    '校正中'] else Update_Db_Data().update_status(data['search_conditions']['patientname'], 4,2,4) if data[
                                                                                                                           'status'] in [
                                                                                                                           '分析中'] else 0


if __name__ == '__main__':
    pytest.main()

[testdata/login/login_testdata.py]

# 登录成功场景
login_sucess = [
    { 'case_name': '用户名和密码正确、登录成功',
      'username': 'ais',
      'password': '123456',
      'expected_results': '登录成功!'
    }
]
# 登录失败场景
login_failure = [
    { 'case_name': '用户名正确、密码错误、登录失败',
      'username': 'ais',
      'password': '12323456',
      'expected_results': '用户名或密码错误'
    },
    { 'case_name': '用户不正确、登录失败',
      'username': 'ais1',
      'password': '123456',
      'expected_results': '用户名或密码错误'
    }
]
# 异常登录场景
login_exception = [
    {'case_name': '用户名为空、登录失败',
     'username': '',
     'password': '12323456',
     'expected_results': '用户名不能为空!'
     },
    { 'case_name': '密码为空、登录失败',
      'username': 'ais',
      'password': '',
      'expected_results': '密码不能为空!'
    }
]

# 系统退出
login_out = [
    {'case_name': '点击退出系统按钮、返回软件登录页',
     'username': 'ais',
     'password': '123456',
     'expected_results': True   #登录页的登录按钮可见
     }
]

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

python-selenium-pytest-allure UI自动化测试 的相关文章

  • 在 python 2 和 3 的spyder之间切换

    根据我在文档中了解到的内容 它指出您只需使用命令提示符创建一个新变量即可轻松在 2 个 python 环境之间切换 如果我已经安装了 python 2 7 则 conda create n python34 python 3 4 anaco
  • OpenCV 错误:使用 COLOR_BGR2GRAY 函数时断言失败

    我在使用 opencv 时遇到了一个奇怪的问题 我在 jupyter 笔记本中工作时没有任何问题 但在尝试运行此 Sublime 时却出现问题 错误是 OpenCV错误 cvtColor中断言失败 深度 CV 8U 深度 CV 16U 深度
  • 如何在groupby之后将pandas数据框拆分为许多列

    我希望能够在 pandas 中使用 groupby 按列对数据进行分组 然后将其拆分 以便每个组都是数据框中自己的列 e g time data 0 1 2 0 1 2 3 0 2 3 4 0 3 1 2 1 4 2 3 1 5 3 4 1
  • 将 Django 表单中的所有 CharField 表单字段输入转换为小写

    我使用 Django 表单进行用户注册 用户可以在其中输入优惠券代码 我希望在优惠券代码字段中输入的所有字符都转换为小写 我尝试过在保存方法 自定义清理方法和自定义验证器中使用 lower 但这些方法没有运气 下面是我的代码 class S
  • 远程控制或脚本打开 Office 从 Python 编辑 Word 文档

    我想 最好在 Windows 上 在特定文档上启动 Open Office 搜索固定字符串并将其替换为我的程序选择的另一个字符串 我该如何从外部 Python 程序中做到这一点 OLE 什么 原生 Python 脚本解决方案 The doc
  • 一起使用 Argparse 和 Json

    我是 Python 初学者 我想知道 Argparse 和 JSON 是否可以一起使用 说 我有变量p q r 我可以将它们添加到 argparse 中 parser add argument p param1 help x variabl
  • 使用 Python 解析 XML,解析外部 ENTITY 引用

    在我的 S1000D xml 中 它指定了一个带有对公共 URL 的引用的 DOCTYPE 该 URL 包含对包含所有有效字符实体的许多其他文件的引用 我使用 xml etree ElementTree 和 lxml 尝试解析它并得到解析错
  • 以编程方式将列名称添加到 numpy ndarray

    我正在尝试将列名称添加到 numpy ndarray 然后按名称选择列 但这不起作用 我无法判断问题是在添加名称时出现 还是在稍后尝试调用它们时出现 这是我的代码 data np genfromtxt csv file delimiter
  • Python 相当于 Bit Twiddling Hacks 中的 C 代码?

    我有一个位计数方法 我正在尝试尽可能快地实现 我想尝试下面的算法位摆弄黑客 http graphics stanford edu seander bithacks html CountBitsSetParallel 但我不知道 C 什么是
  • 如何像在浏览器中一样检索准确的 HTML

    我正在使用 Python 脚本来呈现网页并检索其 HTML 它适用于大多数页面 但对于其中一些页面 检索到的 HTML 不完整 我不太明白为什么 这是我用来废弃此页面的脚本 由于某种原因 每个产品的链接不在 HTML 中 Link http
  • 如何将同步函数包装在异步协程中?

    我在用着aiohttp https github com aio libs aiohttp构建一个 API 服务器 将 TCP 请求发送到单独的服务器 发送 TCP 请求的模块是同步的 对于我来说是一个黑匣子 所以我的问题是这些请求阻塞了整
  • 在 GAE/Python 中放置一次性代码和每次代码的最佳位置在哪里?

    我是 Google App Engine 和 Python 的新手 我无法理解有关在 Google App Engine 上运行的 Python 应用程序的一些基本问题 如果我想要执行代码 对于每个传入的请求 我应该将其放在哪里 我们正在捕
  • 如何在python中递归复制目录并覆盖全部?

    我正在尝试复制 home myUser dir1 及其所有内容 及其内容等 home myuser dir2 在Python中 此外 我希望副本覆盖中的所有内容dir2 It looks like distutils dir util co
  • Python time.sleep - 永不醒来

    我认为这将是那些简单的问题之一 但它让我感到困惑 停止媒体 我是对的 找到了解决方案 查看答案 我正在使用 Python 的单元测试框架来测试多线程应用程序 很好而且很直接 我有 5 个左右的工作线程监视一个公共队列 以及一个为它们制作工作
  • DRF:以编程方式从 TextChoices 字段获取默认选择

    我们的网站是 Vue 前端 DRF 后端 在一个serializer validate 方法 我需要以编程方式确定哪个选项TextChoices类已被指定为模型字段的默认值 TextChoices 类 缩写示例 class PaymentM
  • Windows 与 Linux 文本文件读取

    问题是 我最近从 Windows 切换到 Ubuntu 我的一些用于分析数据文件的 python 脚本给了我错误 我不确定如何正确解决 我当前仪器的数据文件输出如下 Header 有关仪器等的各种信息 Data 状态 代码 温度 字段等 0
  • Melt() 函数复制数据集

    我有一个这样的表 id name doggo floofer puppo pupper 1 rowa NaN NaN NaN NaN 2 ray NaN NaN NaN NaN 3 emma NaN NaN NaN pupper 4 sop
  • 字母尺度和随机文本上的马尔可夫链

    我想使用 txt 文件中的一本书中的字母频率生成随机文本 以便每个新字符 string lowercase 取决于前一个 如何使用马尔可夫链来做到这一点 或者使用每个字母都有条件频率的 27 个数组更简单 我想使用来自的字母频率生成随机文本
  • Django - 缺少 1 个必需的位置参数:'request'

    我收到错误 get indiceComercioVarejista 缺少 1 个必需的位置参数 要求 当尝试访问 get indiceComercioVarejista 方法时 我不知道这是怎么回事 views from django ht
  • Python - 打印漂亮的 XML 为空标签文本创建开始和结束标签

    我正在编写一个 python 应用程序 它创建一个 ElementTree XML 然后使用 minidom 的 toprettyxml 将其写入文件 final tree minidom parseString ET tostring r

随机推荐

  • VScode安装

    1 下载安装VScode 下载地址 Visual Studio Code Code Editing Redefined 浏览器下载很慢 我们进入下载页面 复制下载链接 https az764295 vo msecnd net stable
  • ubunutu20.04 pycharm使用anaconda下环境(主要是pytorch)

    新建pycharm项目和anaconda环境 打开pycharm new project new environment using conda 修改环境名 项目名 python版本选择3 9 我的电脑pyTorch在3 9上跑通了 其他版
  • SAP应付模块详解

    本文介绍以下内容 应付模块的基础知识 主数据 供应商 发票处理 付款及清账 预付款 应付票据 其他特别总账业务 供应商余额查询 定期处理 月末及年末年初的操作 应付模块报表 应付模块设计的流程清单和方案要点 由于应付模块和应收模块在很多方面
  • LaTex 之 数学运算符号

    属于号 in 开根号 sqrt 求和符号 sum 积分符号 int min max 大于等于 小于等于号 导言区使用两个宏包 usepackage amsmath usepackage amssymb 大于等于号 geqslant or g
  • Code Review的亲身实践

    Code Review 中文叫代码审查 指的是完成了部分功能的代码开发之后 在代码真正合并到仓库主分支之前 邀请同事帮你进行代码的审核和检查 检查代码的质量 规范 设计等等方面的过程 代码审查的好处 知识共享 进行代码审查的好处很多 其中一
  • Elasticsearch入门初探-单机多节点集群

    实时搜索引擎Elasticsearch 简称ES 是一个基于Apache Lucene 的开源搜索引擎 无论在开源还是专有领域 Lucene可以被认为是迄今为止最先进 性能最好 这里分享Elasticsearch入门在单机下如何配置多节点集
  • 数据权限——Mybatis拦截器实现

    一 需求背景介绍 1 需求介绍 需要实现数据权限管理 包含角色 普通用户 组长 管理员 其中普通用户只能看到自己创建的项目 组长能看到自己所管理的普通用户创建的项目 管理员能看到所有项目 相关表为 项目表 包含责任人owner字段 owne
  • iconfont下载到本地使用教程

    1 选择Symbol下载至本地 2 将压缩包进行解压 将压缩包如下后缀的文件放入项目中 iconfont css iconfont ttf iconfont woff iconfont woff2 通过import进行导入到index cs
  • 使用python-docx生成word文档

    基于python docx生成自定义word文档 源代码 from docx import Document from docx shared import Pt Inches from docx oxml ns import qn fro
  • Linux 字节序与字节对齐优化

    1 字节序跟Linux Windows无关 是由CPU构架决定 同一个CPU不管装的是Windows 或 Linux 字节序都是一样的 2 字节对齐 Linux 全用 attribute packed 作用于结构体 类似于pragma pa
  • 【深度学习】——循环神经网络RNN及实例气温预测、单层lstm股票预测

    引言 密集连接网络和卷积神经网络都有主要的特点 那就是它们没有记忆 它们单独处理每个输入 在输入和输入之间没有保存任何状态 举个例子 当你在阅读一个句子的时候 你需要记住之前的内容 我们才能动态的了解这个句子想表达的含义 生物智能已渐进的方
  • easyexcel poi根据模板导出Excel

    1 导入依赖
  • 最大和的连续子数组

    1 题目背景 给你一个整数数组nums 请你找出一个具有最大和的连续子数组 子数组最少包含一个元素 返回其最大和 子数组是数组中的一个连续部分 2 代码实现 public class Solution public static void
  • 关于HTTP常见状态码

    http状态码 HTTP Status Code 表示网页服务器超文本传输协议响应状态的3位数代码 是服务器用来告诉客户端当前请求响应的状态 通过状态码判断服务器运行状态 分类的第一位数字来表示 1xx表示消息 2xx表示成功 3xx表示重
  • 指向函数的指针 ------ 函数指针(function pointer)

    函数具有可赋值给指针的物理内存地址 一个函数的函数名就是一个指针 它指向函数的代码 一个函数的地址是该函数的进入点 也是调用函数的地址 函数的调用可以通过函数名 也可以通过指向函数的指针来调用 函数指针还允许将函数作为变元传递给其他函数 不
  • 读写一致 && MySQL&&Redis

    存储一致性 CPU存储 L1L2Cache等等 不懂CPU如何读写内存还敢说自己是程序员 知乎 zhihu com 基础知识 1 什么是cache line cache line 位于 CPU 与内存之间 CPU想要获得数据的时候 先从CP
  • Typora+PicGo+Alist 私人图床教程

    前置资源 已安装好typora 直接官网下载即可 Typora 官方中文站 typoraio cn 安装好PicGo 稳定版优先 推荐稳定版2 3 1 PicGo 腾讯云COS 下载链接 下载速度快 https picgo 12517503
  • 人民币兑换python

    输入一个人民币的整数值 100以内以元为单位 编程找到用10元 5元 2元 1元表示的总数量的最小组合方式 输入形式 从控制台输入一个整数值 表示以元为单位的人民币币值 输出形式 向控制台输出四个整数 以空格分隔 分别表示兑换成的10元 5
  • linux安装minio以及springboot整合使用

    文章目录 1 linux安装minio 2 springboot整合minio使用 1 linux安装minio 1 新建文件夹 mkdir home minio 数据文件夹 mkdir home minio data 创建日志文件夹 mk
  • python-selenium-pytest-allure UI自动化测试

    一 下载及安装插件 python selenium pytest allure 辅助 pyautoit pymysql 二 代码框架 1 基础文件 main py if name main pytest main alluredir all