学习pytest+allureUI自动化记录

Posted mrqius

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了学习pytest+allureUI自动化记录相关的知识,希望对你有一定的参考价值。

前段时间记录了一下pytest接口自动化测试,今天来记录pytest+allureUI自动化了 ,还是直接上代码吧。

目录

case#存放测试用例
common#存放测试公共方法
data#存放测试数据、定位元素
logs#存放日志文件
pages#存放页面页面元素,操作步骤
report#存放测试报告
getpathinfo.py#读取当前目录
pytest#pytest配置文件
requirements.txt#依赖包

学习框架

pytest单元测试框架+allure生成测试报告

结构设计

1.每一个页面的所有用例组合在一个测试类里面生成一个py文件

2.将每个页面用例的操作步骤封装在一个测试类里面生成一个py文件

3.将测试数据,定位元素存放在yml文件中

4.通过allure生成测试报告

学习内容

1.pytes单元测试框架

2.allure生成测试报告

3.yml文件存放测试数据通过parametrize进行参数化

4.无界面运行测试用例

练习代码

getpathinfo.py #获取当前路径

技术图片
import os

def get_path():
    curpath = os.path.dirname(os.path.realpath(__file__))
    return curpath

if __name__ == __main__:
    print("测试路径",get_path())
getpathinfo.py

pytest.ini #存放pytest配置文件

技术图片
#pytest.ini
[pytest]

#addopts = -v --reruns 1 --html=./report/report.html --self-contained-html
#addopts = -v --reruns 1 --alluredir ./report/allure_raw
#addopts = -v -s -p no:warnings --reruns 1 --pytest_report ./report/Pytest_Report.html
pytest.ini

requirements.txt  #存放导入依赖包(pip install -r requirements.txt安装依赖包)

技术图片
allure-pytest==2.8.15
allure-python-commons==2.8.15
appdirs==1.4.4
APScheduler==3.6.3
atomicwrites==1.3.0
attrs==19.3.0
BeautifulReport==0.1.2
beautifulsoup4==4.8.1
black==19.10b0
certifi==2019.9.11
cffi==1.14.0
chardet==3.0.4
Click==7.0
colorama==0.4.3
colorlog==4.1.0
crypto==1.4.1
cryptography==2.9.2
filetype==1.0.7
Flask==1.1.1
har2case==0.3.1
HttpRunner==1.5.8
idna==2.8
importlib-metadata==1.6.0
itsdangerous==1.1.0
Jinja2==2.10.3
jmespath==0.9.5
loguru==0.4.1
lxml==4.5.0
MarkupSafe==1.1.1
more-itertools==8.2.0
Naked==0.1.31
numpy==1.17.4
packaging==20.3
parameterized==0.7.1
ParamUnittest==0.2
pathspec==0.8.0
Pillow==6.2.1
pluggy==0.13.1
py==1.8.1
pycparser==2.20
pycryptodome==3.9.4
pydantic==1.5.1
Pygments==2.6.1
Pymysql==0.9.3
pyparsing==2.4.7
Pypubsub==4.0.3
pytest==5.4.2
pytz==2019.3
pywin32==227
PyYAML==5.1.2
regex==2020.5.14
requests==2.22.0
requests-toolbelt==0.9.1
robotframework==3.1.2
robotframework-ride==1.7.4.1
schedule==0.6.0
selenium==3.141.0
send-email==201212
shellescape==3.4.1
six==1.13.0
soupsieve==1.9.5
toml==0.10.1
typed-ast==1.4.1
tzlocal==2.0.0
urllib3==1.25.7
wcwidth==0.1.9
Werkzeug==0.16.0
win32-setctime==1.0.1
wxPython==4.0.7.post2
xlrd==1.2.0
zipp==3.1.0
requirements.txt

common/base.py #存放公共方法

技术图片
from selenium import webdriver
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from  selenium.webdriver.support.select import Select
from selenium.webdriver.common.action_chains import ActionChains
from selenium.common.exceptions import NoSuchElementException
from selenium.common.exceptions import NoSuchFrameException
from selenium.common.exceptions import StaleElementReferenceException
from selenium.common.exceptions import ElementNotVisibleException
from selenium.common.exceptions import TimeoutException
from common.log import Log

class LocatorTypeError(Exception):
    pass
class ElementNotFound(Exception):
    pass

class Base():
    ‘‘‘基于原生selenium二次封装‘‘‘
    log = Log()
    def __init__(self,driver:webdriver.Chrome,timeout = 10,t = 0.5):
        url = "http://********"
        self.base_url = url
        self.driver = driver
        self.timeout = timeout
        self.t = t

    def find(self, locator):
        """定位到元素,返回元素对象,没定位到,Timeout异常"""
        if not isinstance(locator, tuple):
            raise LocatorTypeError("参数类型错误,locator必须是元祖类型:loc = (‘id‘,‘value1‘)")
        else:
            self.log.info("正在定位元素信息:定位方式->%s,value值->%s" % (locator[0], locator[1]))
            #print("正在定位元素信息:定位方式->%s,value值->%s" % (locator[0], locator[1]))
            try:
                ele = WebDriverWait(self.driver, self.timeout, self.t).until(EC.presence_of_element_located(locator))
            except TimeoutException as msg:
                 raise ElementNotFound("定位元素出现超时!")
            return ele

    def finds(self,locator):
        ‘‘‘复数定位,返回elements对象 list‘‘‘
        if not isinstance(locator,tuple):
            raise LocatorTypeError(参数类型错误,locator必须是元组类型:loc = ("id","value"))
        else:
            self.log.info("正在定位元素信息:定位方式->%s,value值->%s" % (locator[0], locator[1]))
            #print("正在定位元素信息:定位方式->%s,value值->%s"%(locator[0],locator[1]))
            try:
                eles = WebDriverWait(self.driver, self.timeout, self.t).until(EC.presence_of_all_elements_located(locator))
            except TimeoutException as msg:
                raise ElementNotFound("定位元素出现超时!")
            return eles

    def writein(self,locator,text = ""):
        ‘‘‘写入文本‘‘‘
        ele = self.find(locator)
        if ele.is_displayed():
            ele.send_keys(text)
        else:
            raise ElementNotVisibleException("元素不可见或者不唯一无法输入")

    def click(self,locator):
        ‘‘‘点击元素‘‘‘
        ele = self.find(locator)
        if ele.is_displayed():
            ele.click()
        else:
            raise ElementNotVisibleException("元素不可见或者不唯一无法点击")

    def clear(self,locator):
        ‘‘‘清空输入框文本‘‘‘
        ele = self.find(locator)
        if ele.is_displayed():
            ele.clear()
        else:
            raise ElementNotVisibleException("元素不可见或者不唯一")

    def is_selected(self,locator):
        ‘‘‘判断元素是否被选中,返回bool值‘‘‘
        ele  = self.find(locator)
        r = ele.is_selected()
        return r

    def is_element_exist(self,locator):
        ‘‘‘是否找到‘‘‘
        try:
            self.find(locator)
            return True
        except :
            return False

    def is_title(self,title = ""):
        ‘‘‘返回bool值‘‘‘
        try:
            result = WebDriverWait(self.driver,self.timeout,self.t).until(EC.title_is(title))
            return result
        except :
            return False

    def is_title_contains(self, title=‘‘):
        """返回bool值"""
        try:
            result = WebDriverWait(self.driver, self.timeout, self.t).until(EC.title_contains(title))
            return result
        except:
            return False

    def is_text_in_element(self,locator,text = ‘‘):
        ‘‘‘返回bool值‘‘‘
        if not isinstance(locator,tuple):
            raise LocatorTypeError("参数类型错误,locator必须是元祖类型:loc = (‘id‘,‘value1‘)")
        try:
            result = WebDriverWait(self.driver, self.timeout, self.t).until(
                EC.text_to_be_present_in_element(locator, text))
            return result
        except :
            return False

    def is_value_in_element(self,locator,value = ""):
        if not isinstance(locator, tuple):
            raise LocatorTypeError("参数类型错误,locator必须是元祖类型:loc = (‘id‘,‘value1‘)")
        try:
            result = WebDriverWait(self.driver, self.timeout, self.t).until(
                EC.text_to_be_present_in_element_value(locator, value))
            return result
        except:
            return False

    def is_alert(self,timeout = 8):
        try:
            result = WebDriverWait(self.driver, timeout, self.t).until(EC.alert_is_present())
            return result
        except:
            return False

    def get_title(self):
        """获取title"""
        return self.driver.title

    def get_text(self, locator):
        """获取文本"""
        if not isinstance(locator, tuple):
            raise LocatorTypeError("参数类型错误,locator必须是元祖类型:loc = (‘id‘,‘value1‘)")
        try:
            t = self.find(locator).text
            return t
        except:
            self.log.info("获取text失败,返回‘‘")
            #print("获取text失败,返回‘‘")
            return ""

    def get_attribute(self, locator, name):
        """获取属性"""
        if not isinstance(locator, tuple):
            raise LocatorTypeError("参数类型错误,locator必须是元祖类型:loc = (‘id‘,‘value1‘)")
        try:
            element = self.find(locator)
            return element.get_attribute(name)
        except:
            self.log.info("获取%s属性失败,返回‘‘" % name)
            #print("获取%s属性失败,返回‘‘" % name)
            return ‘‘

    def js_focus_element(self,locator):
        ‘‘‘聚焦元素‘‘‘
        if not isinstance(locator,tuple):
            raise LocatorTypeError("参数类型错误")
        target = self.find(locator)
        self.driver.execute_script("arguments[0].scrollIntoView();", target)

    def js_scroll_top(self):
        ‘‘‘滚到顶部‘‘‘
        js = "window.scrollTo(0,0)"
        self.driver.execute_script(js)

    def js_scroll_end(self,x = 0):
        ‘‘‘滚到底部‘‘‘
        js = "window.scrollTo(%s, document.body.scrollHeight)" % x
        self.driver.execute_script(js)

    def select_by_index(self,locator,index =0):
        ‘‘‘通过索引,index是索引第几个,从0开始,默认第一个‘‘‘
        if not isinstance(locator,tuple):
            raise LocatorTypeError("参数类型错误")
        element = self.find(locator)
        Select(element).select_by_index(index)

    def select_by_value(self, locator, value):
        """通过value属性"""
        if not isinstance(locator, tuple):
            raise LocatorTypeError("参数类型错误")
        element = self.find(locator)
        Select(element).select_by_value(value)

    def select_by_text(self,locator,text):
        """通过文本值定位"""
        element = self.find(locator)
        Select(element).select_by_visible_text(text)

    def switch_iframe(self, id_index_locator):
        """切换iframe"""
        try:
            if isinstance(id_index_locator, int):
                self.driver.switch_to.frame(id_index_locator)
            elif isinstance(id_index_locator, str):
                self.driver.switch_to.frame(id_index_locator)
            elif isinstance(id_index_locator, tuple):
                ele = self.find(id_index_locator)
                self.driver.switch_to.frame(ele)
        except:
            self.log.info("iframe切换异常")
            #print("iframe切换异常")

    def switch_handle(self,window_name):
        self.driver.switch_to.window(window_name)

    def switch_alert(self):
        r = self.is_alert()
        if not r:
            self.log.info("alert不存在")
            #print("alert不存在")
        else:
            return r

    def move_to_element(self, locator):
        """鼠标悬停操作"""
        if not isinstance(locator, tuple):
            raise LocatorTypeError("参数类型错误")
        ele = self.find(locator)
        ActionChains(self.driver).move_to_element(ele).perform()

if __name__ == __main__:
    driver = webdriver.Chrome()
    web = Base(driver)
    driver.get("https://www.baidu.com")
    loc_1 = ("id", "kw")
    web.writein(loc_1, "hello")
    driver.close()
base.py

common/log.py #生成日志文件

技术图片
import logging,time
import os
import getpathinfo

path = getpathinfo.get_path()#获取本地路径
log_path = os.path.join(path,logs)# log_path是存放日志的路径
# 如果不存在这个logs文件夹,就自动创建一个
if not os.path.exists(log_path):os.mkdir(log_path)

class Log():
    def __init__(self):
        #文件的命名
        self.logname = os.path.join(log_path,%s.log%time.strftime(%Y_%m_%d))
        self.logger = logging.getLogger()
        self.logger.setLevel(logging.DEBUG)
        #日志输出格式
        self.formatter = logging.Formatter(%(asctime)s - %(name)s - %(levelname)s - %(message)s)

    def __console(self,level,message):
        #创建一个fileHander,用于写入本地
        fh = logging.FileHandler(self.logname,a,encoding=utf-8)
        fh.setLevel(logging.DEBUG)
        fh.setFormatter(self.formatter)
        self.logger.addHandler(fh)

        #创建一个StreamHandler,用于输入到控制台
        ch = logging.StreamHandler()
        ch.setLevel(logging.DEBUG)
        ch.setFormatter(self.formatter)
        self.logger.addHandler(ch)

        if level == info:
            self.logger.info(message)
        elif level == debug:
            self.logger.debug(message)
        elif level == warning:
            self.logger.warning(message)
        elif level == error:
            self.logger.error(message)
        #避免日志重复
        self.logger.removeHandler(fh)
        self.logger.removeHandler(ch)
        #关闭打开文件
        fh.close()

    def debug(self,message):
        self.__console(debug,message)

    def info(self,message):
        self.__console(info,message)

    def warning(self,message):
        self.__console(warning,message)

    def error(self,message):
        self.__console(error,message)

if __name__ == __main__:
    log = Log()
    log.info(测试)
    log.debug(测试)
    log.warning(测试)
    log.error(测试)
log.py

common/read_yml.py #读取yml文件

技术图片
import os
import yaml
import getpathinfo

class ReadYaml():
    def __init__(self,filename):
        path = getpathinfo.get_path()#获取本地路径
        self.filepath = os.path.join(path,data)+"/"+filename#拼接定位到data文件夹

    def get_yaml_data(self):
        with open(self.filepath,r,encoding=utf-8)as f:
            #调用load方法加载文件流
            return yaml.load(f,Loader=yaml.FullLoader)

if __name__ == __main__:
    data = ReadYaml("login_page.yml").get_yaml_data()
    username = data["test_login_element"][0]
    print(tuple(username))
read_yml.py

pages #存放测试页面操作步骤、断言(写了简单两个)

技术图片
from common.base import Base
from common.read_yml import ReadYaml
testelement = ReadYaml("login_page.yml").get_yaml_data()

class LoginPage(Base):
    loc1 = tuple(testelement["test_login_element"][0])#用户名
    loc2 = tuple(testelement["test_login_element"][1])#密码
    loc3 = tuple(testelement["test_login_element"][2])#登录
    # 判断元素
    loc4 = tuple(testelement["test_login_element"][3])#登录成功断言
    loc5 = tuple(testelement["test_login_element"][4])#登录失败断言

    def input_username(self, text="admin"):
        ‘‘‘输入用户名‘‘‘
        self.writein(self.loc1, text)

    def input_password(self, text="yoyo123456"):
        ‘‘‘输入用户名‘‘‘
        self.writein(self.loc2, text)

    def click_button(self):
        ‘‘‘点击登录按钮‘‘‘
        self.click(self.loc3)

    def login(self, user="admin", password="yoyo123456"):
        ‘‘‘登录‘‘‘
        self.driver.get(self.base_url)
        self.input_username(user)
        self.input_password(password)
        self.click_button()

    def is_login_success(self, expect_text=后台页面):
        text = self.get_text(self.loc4)
        self.log.info("获取到断言元素的文本内容:%s"%text)
        return expect_text == text

    def is_login_fail(self, expect_text=请输入正确的用户名和密码):
        text = self.get_text(self.loc5)
        self.log.info("获取到断言元素的文本内容:%s"%text)
        return expect_text in text

if __name__ == __main__:
    from selenium import webdriver
    driver = webdriver.Chrome()
    web = LoginPage(driver)
    web.login()
    result = web.is_login_fail()
    print("登录结果:", result)
    assert result
    driver.quit()
login_page.py
技术图片
from common.base import Base
from common.read_yml import ReadYaml
testelement = ReadYaml("add_account_page.yml").get_yaml_data()

class Add_Account(Base):

    loc1 = tuple(testelement["test_account_element"][0])  # 银行卡账户
    loc2 = tuple(testelement["test_account_element"][1])  # 添加银行卡账户
    loc3 = tuple(testelement["test_account_element"][2])  # 卡号
    loc4 = tuple(testelement["test_account_element"][3])  # 姓名
    loc5 = tuple(testelement["test_account_element"][4])  # 电话
    loc6 = tuple(testelement["test_account_element"][5])  # 邮箱
    loc7 = tuple(testelement["test_account_element"][6])  # 城市
    loc8 = tuple(testelement["test_account_element"][7])  # 性别
    loc9 = tuple(testelement["test_account_element"][8])  # 保存
    loc10 = tuple(testelement["test_account_element"][9]) # 新增成功校验
    loc11 = tuple(testelement["test_account_element"][10]) #账号为空校验
    loc12 = tuple(testelement["test_account_element"][11]) #姓名为空校验

    def click_account(self):
        ‘‘‘点击银行卡账号‘‘‘
        self.finds(self.loc1)[0].click()

    def click_add_account(self):
        ‘‘‘点击添加银行卡账号‘‘‘
        self.click(self.loc2)

    def input_card_num(self,text = "123456"):
        ‘‘‘输入卡号‘‘‘
        self.writein(self.loc3,text)

    def input_name(self,text = " "):
        ‘‘‘输入姓名‘‘‘
        self.writein(self.loc4,text)

    def input_phone(self,text = "123456"):
        ‘‘‘输入电话‘‘‘
        self.writein(self.loc5,text)

    def input_mail(self,text = "123456"):
        ‘‘‘输入邮箱‘‘‘
        self.writein(self.loc6,text)

    def input_city(self,text = "测试"):
        ‘‘‘输入城市‘‘‘
        self.writein(self.loc7,text)

    def input_sex(self,text = ""):
        ‘‘‘输入性别‘‘‘
        self.writein(self.loc8,text)

    def click_save(self):
        ‘‘‘点击保存‘‘‘
        self.click(self.loc9)

    def is_add_success(self, expect_text=添加成功):
        text = self.get_text(self.loc10)
        self.log.info("获取到断言元素的文本内容:%s" %text)
        return expect_text in text

    def is_add_fail1(self, expect_text=这个字段是必须的):
        ‘‘‘账号为空断言‘‘‘
        text = self.get_text(self.loc11)
        self.log.info("获取到断言元素的文本内容:%s" %text)
        return expect_text in text

    def is_add_fail2(self, expect_text=这个字段是必须的):
        ‘‘‘姓名为空断言‘‘‘
        text = self.get_text(self.loc12)
        self.log.info("获取到断言元素的文本内容:%s" %text)
        return expect_text in text

if __name__ == __main__:
    from pages.login_page import LoginPage
    from selenium import webdriver
    driver = webdriver.Chrome()
    driver.maximize_window()
    web = LoginPage(driver)
    web.login()
    account = Add_Account(driver)
    account.click_account()
    account.click_add_account()
    account.input_card_num()
    account.input_name()
    account.input_phone()
    account.input_mail()
    account.input_city()
    account.input_sex()
    account.click_save()
    account.is_add_fail()
    driver.quit()
    
add_account_page.py

case/conftest.py #存放前置操作,设置无界面模式

技术图片
import platform

from selenium import webdriver
from pages.login_page import LoginPage
import pytest
import time
from common.log import Log
from selenium.webdriver.chrome.options import Options
log = Log()
@pytest.fixture(scope="session")
def login_fixtrue(driver):
    #登录前置操作
    #driver = webdriver.Chrome()
    #driver.maximize_window()
    web = LoginPage(driver)
    web.login()
    return driver
def pytest_addoption(parser):
    ‘‘‘添加命令行参数‘‘‘
    parser.addoption(--headless, action = "store",
                     default = no, help = set chrome headless option yes or no
    )
@pytest.fixture(scope="session")
def driver(request):
    """定义全局driver fixture,给其它地方作参数调用"""
    if platform.system()==Windows:
        chrome_options = Options()
        chrome_options.add_argument(--window-size=1920,1080)  # 设置当前窗口的宽度,高度
        chrome_options.add_argument(--headless)  # 无界面
        print("当前运行的操作系统为windows")
        _driver = webdriver.Chrome(chrome_options=chrome_options)
    else:
        print(当前运行的操作系统为linux)
        chrome_options = Options()
        chrome_options.add_argument(--window-size=1920,1080)  # 设置当前窗口的宽度,高度
        chrome_options.add_argument(--no-sandbox)#解决DevToolsActivePort文件不存在报错问题
        chrome_options.add_argument(--disable-gpu)#禁用GPU硬件加速,如果软件渲染器没有就位,则GPU进程将不会启动
        chrome_options.add_argument(--disable-dev-shm-usage)
        chrome_options.add_argument(--headless)  # 无界面
        _driver = webdriver.Chrome(chrome_options=chrome_options)

    def end():
        print("全部用例执行完后 teardown quit dirver")
        time.sleep(5)
        _driver.quit()
    request.addfinalizer(end)
    return _driver
conftest.py

case/test_login.py #存放登录页面测试用例

技术图片
import allure
import pytest
from common.log import Log
from common.read_yml import ReadYaml
from pages.login_page import LoginPage
from selenium import webdriver
testdata = ReadYaml(login_page.yml).get_yaml_data()#读取数据

class Test_login():

    log = Log()

    @allure.feature("功能点:用户登录页面")
    @allure.story("用例:用户登录")
    @pytest.mark.parametrize("username,password,msg",testdata["test_login_success_data"],
                             ids = ["正确用户名密码登录"])
    @pytest.mark.skip(跳过该成功用例)
    def test_success_login(self,driver,username,password,msg):
        #driver = webdriver.Chrome()
        web = LoginPage(driver)
        web.login(user=username,password=password)
        result = web.is_login_success(expect_text=msg)
        self.log.info("登录结果:%s"%result)
        assert result
        #driver.quit()

    @allure.feature("功能点:用户登录页面")
    @allure.story("用例:用户登录")
    @pytest.mark.parametrize("username,password,msg", testdata["test_login_fail_data"],
                             ids=["正确用户名错误密码登录",
                                  "错误用户名正确密码登录"])
    def test_fail_login(self,driver,username,password,msg):
        #driver = webdriver.Chrome()
        web = LoginPage(driver)
        web.login(user=username,password=password)
        result = web.is_login_fail(expect_text=msg)
        self.log.info("登录结果:%s"%result)
        assert result
test_login.py

case/test_add_account.py #存放添加账户页面测试用例

技术图片
import allure
import pytest
from common.log import Log
from common.read_yml import ReadYaml
from pages.add_account_page import Add_Account
testdata = ReadYaml(add_account_page.yml).get_yaml_data()#读取测试数据

class Test_Add_Account():
    log = Log()

    @allure.feature("功能点:添加银行卡账户")
    @allure.story("用例:添加银行卡账户")
    @pytest.mark.parametrize("card_num,name,phone,mail,city,sex,msg",testdata["test_add_account_data1"],
                             ids=["正常添加"])
    def test_add_account(self, login_fixtrue,card_num, name, phone,mail,city,sex,msg):
        driver = login_fixtrue
        account = Add_Account(driver)
        with allure.step("点击银行卡账户,跳转添加账户页面"):
            account.click_account()
        with allure.step("点击添加账户按钮,进入编辑页面"):
            account.click_add_account()
        with allure.step("输入账户号"):
            account.input_card_num(text=card_num)
        with allure.step("输入姓名"):
            account.input_name(text=name)
        with allure.step("输入手机号"):
            account.input_phone(text=phone)
        with allure.step("输入邮箱"):
            account.input_mail(text=mail)
        with allure.step("输入城市"):
            account.input_city(text=city)
        with allure.step("输入性别"):
            account.input_sex(text=sex)
        with allure.step("点击保存"):
            account.click_save()
        with allure.step("获取结果: 获取页面实际结果,判断是否添加成功"):
            result = account.is_add_success(expect_text=msg)
            self.log.info("断言结果:%s"%result)
        with allure.step("断言:判断是否添加成功"):
            assert result == True
        #driver.quit()

    @allure.feature("功能点:添加银行卡账户")
    @allure.story("用例:添加银行卡账户")
    @pytest.mark.parametrize("card_num,name,phone,mail,city,sex,msg",testdata["test_add_account_data2"],
                             ids=["账号为空添加"])
    def test_add_account1(self, login_fixtrue,card_num, name, phone,mail,city,sex,msg):
        driver = login_fixtrue
        account = Add_Account(driver)
        with allure.step("点击银行卡账户,跳转添加账户页面"):
            account.click_account()
        with allure.step("点击添加账户按钮,进入编辑页面"):
            account.click_add_account()
        with allure.step("输入账户号"):
            account.input_card_num(text=card_num)
        with allure.step("输入姓名"):
            account.input_name(text=name)
        with allure.step("输入手机号"):
            account.input_phone(text=phone)
        with allure.step("输入邮箱"):
            account.input_mail(text=mail)
        with allure.step("输入城市"):
            account.input_city(text=city)
        with allure.step("输入性别"):
            account.input_sex(text=sex)
        with allure.step("点击保存"):
            account.click_save()
        with allure.step("获取结果: 获取页面实际结果,判断是否添加成功"):
            result = account.is_add_fail1(expect_text=msg)
            self.log.info("断言结果:%s"%result)
        with allure.step("断言:判断是否添加成功"):
            assert result == True
        #driver.quit()

    @allure.feature("功能点:添加银行卡账户")
    @allure.story("用例:添加银行卡账户")
    @pytest.mark.parametrize("card_num,name,phone,mail,city,sex,msg", testdata["test_add_account_data3"],
                             ids=["姓名为空添加"])
    def test_add_account2(self, login_fixtrue, card_num, name, phone, mail, city, sex, msg):
        driver = login_fixtrue
        account = Add_Account(driver)
        with allure.step("点击银行卡账户,跳转添加账户页面"):
            account.click_account()
        with allure.step("点击添加账户按钮,进入编辑页面"):
            account.click_add_account()
        with allure.step("输入账户号"):
            account.input_card_num(text=card_num)
        with allure.step("输入姓名"):
            account.input_name(text=name)
        with allure.step("输入手机号"):
            account.input_phone(text=phone)
        with allure.step("输入邮箱"):
            account.input_mail(text=mail)
        with allure.step("输入城市"):
            account.input_city(text=city)
        with allure.step("输入性别"):
            account.input_sex(text=sex)
        with allure.step("点击保存"):
            account.click_save()
        with allure.step("获取结果: 获取页面实际结果,判断是否添加成功"):
            result = account.is_add_fail2(expect_text=msg)
            self.log.info("断言结果:%s" % result)
        with allure.step("断言:判断是否添加成功"):
            assert result == True
        # driver.quit()
test_add_account.py

data/add_account_page.yml #存放添加账户页面测试数据,定位元素

技术图片
test_account_element:
  - ["xpath",//*[@href="/xadmin/hello/card/"]]
  - ["xpath",//*[@id="content-block"]/div[1]/div[2]/div/a]
  - ["xpath",//*[@id="id_card_id"]]
  - ["xpath",//*[@id="id_card_user"]]
  - ["xpath",//*[@id="id_carddetail-0-tel"]]
  - ["xpath",//*[@id="id_carddetail-0-mail"]]
  - ["xpath",//*[@id="id_carddetail-0-city"]]
  - ["xpath",//*[@id="id_carddetail-0-address"]]
  - ["xpath",//*[@id="card_form"]/div[2]/button]
  - ["xpath",//*[@id="content-block"]/div[2]]
  - ["xpath",//*[@id="error_1_id_card_id"]]
  - ["xpath",//*[@id="error_1_id_card_user"]]

test_add_account_data1:
  -  ["123456","秋水1","123456","123456","测试","","添加成功"]
test_add_account_data2:
  -  [" ","秋水2","123456","123456","测试","","这个字段是必须的"]
test_add_account_data3:
  -  ["123456"," ","123456","123456","测试","","这个字段是必须的"]
add_account_page.yml

report/allure_raw #测试报告(运行命令:allure serve report/allure_raw)

技术图片

 

 

记录完毕,生成allure报告在pytest.ini中有写,解开注释即可,pytest运行生成报告,或者pytest --alluredir ./report/allure_raw 

以上是关于学习pytest+allureUI自动化记录的主要内容,如果未能解决你的问题,请参考以下文章

pytest框架之fixture详细使用

pytest学习和使用11-Pytest如何使用自定义标记mark?

pytest接口自动化测试框架 | 用python代码测试接口

uCOS-III 学习记录(10)——时间片轮转调度

Pytest 可视化测试报告之 Allure

19-pytest-allure-pytest环境搭建