整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

阿六带你用python appium搭建app自动化测试环境

我们在appium做app自动化测试,环境搭建是比较麻烦的。也是很多初学者在学习app自动化之时,花很多时间都难跨越的坎。今天小编就来讲讲appium如何搭建APP自动化测试环境。喜欢小编的可以给小编点点关注哟。

没有成功的环境,就没有办法继续后续的使用。

在app自动化测试当中,我们主要是通用电脑端的python代码,能够驱使手机端的app去进行操作。比如打开一个app,输入用户名和密码,进入登陆操作。

由于电脑端和手机端是两个独立的设备。要实现数据通信,就需要将二者连一起。因此对于真机用户,需要用USB线连接手机和电脑,并且在开发者选项当中,开启USB调试模式。

app自动化环境安装包括以下几点:

1、安装node.js

2、安装appium desktop程序

3、安装JDK1.8及以上版本

4、安装安卓开发工具

5、安装appium python客户端

6、真机或者模拟器

第一步:安装nodejs和appium desktop程序

nodejs官网下载地址:https://nodejs.org/en/download/

appium的运行依赖于nodejs,所以要先安装nodejs。nodejs下载完成之后,windows用户双击安装完成即可,不需要做额外配置。

appium desktop下载官网:https://github.com/appium/appium-desktop/releases

appium版本更新较快。最新的版本在你的电脑上不一定能运行起来。如果新版本运行不起来,那么建议换其它的版本。

安装也非常简单,双击exe程序运行即可。

第二步:安装JDK

JDK要求1.8版本以及以上。参考网上其它文章来安装jdk。

JDK下载地址:https://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html

第三步:安装安卓开发工具

自动化测试当中,会用到一些工具,但不会去开发一个app。所以sdk部分不需要。只需要下载工具部分即可。

安卓sdk下载地址:http://tools.android-studio.org/index.php/sdk

安装步骤如下:

1)解压 platform-tools_r28.0.2-windows.zip到本地目录

2)双击SDKManager.exe

3)在SDK Manager界面当中,只勾选:

Tools当中的Android SDK Tools、Android SDK Platform-tools、Android SDK Build-tools

Extras目录。

其它的一概不勾选。默认选中的请注意取消勾选!!!!

打开SDK Manager,默认会选中安卓sdk,请一定要取消掉。

4)配置环境变量:

4.1 添加ANDROID_HOME环境变量,配置sdk根目录。

在以上的截图中,

ANDROID_HOME=D:\android-sdk-windows

4.2 在PATH变量中添加三项内容:

%ANDROID_HOME%\platform-tools

%ANDROID_HOME%\tools

%ANDROID_HOME%\build-tools.0.3

检测:

进入cmd命令行,输入adb version

能够正常显示adb的版本就okay.


第四步:安装appium python客户端

使用python的pip命令,直接在线安装:

pip install Appium-Python-Client

第五步:安装模拟器(使用真机的用户,可以跳过此步骤)

本步骤中,选择夜神模拟器。

夜神模拟器官方网站下载地址:https://www.yeshen.com/

下载安装完成之后。桌面会有2个图标:夜神模拟器、夜神多开器。

夜神模拟器:默认安卓版本为4.4.2

夜神多开器:可以增加安卓5.1模拟器、安卓7.1模拟器。

在启动模拟器之前,替换模拟器安装路径当中的nox_adb.exe

第一步:将%ANDROID_HOME%\platform-tools目录下的adb.exe拷贝到桌面,更改名称为nox_adb.exe

第二步:将第一步中的nox_adb拷贝到夜神模拟器安装目录下,替换原来的文件。

再去启动夜神模拟器。然后在cmd命令行当中,输入命令:adb devices


以上所有步骤安装完成之后,恭喜你,环境搭建好了!可以开启app自动化体验之旅了!!

喜欢的可以点赞关注小编哟!

识Git版本控制

自动化测试代码反复执行,如果借用持续集成工具会提高测试效率,那么需要我们把自动化测试代码发布到正式环境中,这时候用Git版本控制工具高效、稳定、便捷。

分布式版本控制

Git可以把代码仓库完整地镜像下来,有完整的历史记录,它可以与远端代码库进行交互。

简史

Git诞生于2005年,速度快,极适合管理大项目。

Git是什么

其他版本控制系统如SVN,是随时间变化的差异性文件比较,在某时间段某些文件进行更新。

Git是快照流,存储项目随时间改变的快照,几乎大部分操作都只需要访问本地文件和资源。保证完整性,用哈希Hash,一般只添加数据,不删除。

Git三种状态:

已修改:在工作区修改文件,但没保存到数据库

已暂存: 在暂存区对已修改的文件做标记,使其包含在下次提交的快照中

已提交:在Git目录安全地保存数据到本地数据库

基本Git工作流程:

①工作区修改文件;

②暂存区将下次提交的更改选择性暂存,修改部分添加到暂存区;

③提交更新,找到暂存区的文件,将快照永久保存在Git目录。

命令行

所有人都有命令行工具,会命令行则应该会GUI。

安装Git

本次实践安装的最新版2.36.0,官网下载windows版本https://git-scm.com/download/win。

初次运行Git前的配置

1)设置用户信息,以下命令:

git config --global user.name “”

git config --global user.email “”

如果想给特定项目设置用户信息,则在该项目目录下运行无--global的命令。

2)设置文本编辑器,如未配置,Git会使用操作系统自带的编辑器。

设置notepad++为文本编辑器:

git config --global core.editor “’d:\dev\notepad++\notepad++.exe” -multiInst -notabbar -nosession -noPlugin”

3)检查配置信息:

git config --list

可逐项检查,如:

git config username

获得帮助

git help +Verb,例如git help config。如果不用全面只需快速参考,使用命令git +Verb -h,例如git add -h。

Git基础

获取Git仓库

两种获得Git项目仓库的方式:

①对尚未进行版本控制的本地目录转换为Git仓库;

②从其他服务器克隆一个已存在的Git仓库。

(1)对已存在目录中初始化仓库

如果有一个尚未进行版本控制的项目目录,想要用Git控制它,那么需要进入该项目目录中。

在Windows上:

cd c/user/my_project

之后执行:

git init

该命令将创建名为.git的子目录,这个子目录含有你做初始化的Git仓库中所有的必须文件,这些文件是Git仓库的骨干。这时,我们仅仅是做了一个初始化的操作,你的项目里的文件还没有被跟踪。

如图所示:

图1

图2

图3

如果在一个已存在文件的文件夹(非空文件夹)中进行版本控制,应该开始追踪这些文件并进行初始提交。

可以通过git add命令指定所需的文件进行追踪,然后执行git commit:

git add *.c

git add LICENSE

git commit -m ‘initial project version’

如图所示:

图4

这样已经得到了一个存在被追踪文件与初始提交的Git仓库。

(2)克隆现有的仓库

如果想获得一份已经存在了的Git仓库的拷贝,比如说,想为某个开源项目贡献自己的一份力,这时就要用到git clone命令。

如果对其他VCS系统(比如Subversion)很熟悉,请留心以下所使用的命令是“clone”而不是“checkout”。这是Git区别于其他版本控制系统的一个重要特性,Git克隆的是该Git仓库服务器上的几乎所有数据,而不是仅仅复制完成你的工作所需要文件。

当你执行git clone命令的时候,默认配置下远程Git仓库中的每一个文件的每个版本都将被拉取下来。实际上,如果服务器磁盘坏掉了,通常可以使用任何一个克隆下来的用户端来重建服务器上的仓库(虽然可能会丢失某些服务器端的hook设置,但是所有版本的数据仍在)。

克隆仓库的命令是git clone。比如,要克隆Git的自建项目HTMLTestRunner,可以用下面的命令:

git clone https://github.com/mingming1205/HTMLTestRunner

这会在当前目录下创建一个名为“HTMLTestRunner”的目录,并在这个目录下初始化一个.git文件夹,从远程仓库拉取下所有数据放入.git文件夹,然后从中读取最新版本的文件的拷贝。

如果打开这个新建的HTMLTestRunner文件夹,会发现所有项目文件已经在里面了,准备就绪等待后续的开发和使用。

如下图:

图5

图6

如果想在克隆远程仓库的时候,自定义本地仓库的名字,可以通过额外的参数指定新的目录名:

git clone https://github.com/mingming1205/HTMLTestRunner myhtmltestrunner

这会执行与上一条命令相同的操作,但是目标目录名变成了myhtmltestrunner。

如下图:

图7

图8

Git支持多种数据传输协议。以上的例子使用的是https://协议,不过也可以使用git://协议或者使用 SSH 传输协议,例如user@server:path/to/repo.git。

至于所有这些协议在服务器端是如何配置使用,以及各种方式之间的利弊请查看“在服务器上搭建 Git”的介绍。

记录每次更新到仓库

执行命令:

git commit -m “描述”

查看提交历史

执行命令:

git log

如果附带一系列总结的文字,执行命令:

git log --stat

如果限制日志查看数量,执行命令git log -p -2,当退出log时,在冒号“:”后面敲q即可。

以下表格引自官网:

感谢大家的阅读,希望你们能从中有所收获!

最后:

1)关注+私信回复:“测试”,可以免费领取一份10G软件测试工程师面试宝典文档资料。以及相对应的视频学习教程免费分享!,其中包括了有基础知识、Linux必备、Mysql数据库、抓包工具、接口测试工具、测试进阶-Python编程、Web自动化测试、APP自动化测试、接口自动化测试、测试高级持续集成、测试架构开发测试框架、性能测试等。

2)关注+私信回复:"入群" 就可以邀请你进入软件测试群学习交流~~

今天我们来讲讲Pytest+Allure+Jenkins实现Web自动化集成测试,喜欢的小伙伴记得点赞哟。

Pytest介绍

pytest是一个非常成熟的全功能的Python测试框架,主要有以下几个特点:简单灵活、容易上手、文档丰富;支持参数化,可以细粒度地控制被测用例;能够支持简单的单元测试和复杂的功能测试,还可以用来做Web自动化(selenium)、APP自动化(appium)以及接口自动化(pytest+requests);pytest具有很多第三方插件,并且可以自动以扩展,比较好用的如pytest-html(完美html测试报告生成)、pytest-rerunfailures(失败用例重复执行)、pytest-xdist(多CPU分发)等;测试用例的skip和xfail处理;可以很好的和CI工具结合,如:Jenkins。

Pytest安装

直接在命令窗口执行以下命令即可,更多信息可查阅官方文档

pip install pytest # 安装pytest
pytest --version   # 查看pytest版本

直接在PyCharm中安装,下面提到的使用pip安装方式都可以直接在PyCharm中安装,安装成功后点击【OK】

PyCharm中设置使用pytest

由于PyCharm默认使用Unitest,所以安装pytest后需要更改默认值,配置路径如下

配置路径:依次点击【File】→【Settings】→【Tools】→【Python Integrated Tools】,【Default test runner】选择“pytest”,然后点击【Apply】→【OK】,就可以使用Pytest啦

Pytest使用方法

Pytest规则

编写pytest测试用例默认使用以下规则,当然,规则是可以修改的

  • 测试文件以test_开头或者以_test结尾
  • 测试类以Test开头,且类中不能包含__init__方法
  • 测试函数/方法以test_开头
  • 断言使用基本的assert即可

修改规则需添加配置文件pytest.ini,它是pytest的主配置文件,可以修改pytest的默认行为,此文件放在项目的根目录下才会生效(除非指定目录查找配置文件),如下配置,注意:实际配置文件中不可出现中文、冒号、引号等

[pytest]
python_files = test_*.py *_test.py case_*	# 添加以case_*开头的测试模块
python_classes = Test* *_case* 				# 添加以*_case结尾的测试类
python_functions = test_* case_* 			# 添加以case_*开头的测试函数
testpaths = testcases	# 指定测试目录,是指以testpaths所在目录为基准的相对路径
addopts = -vs -l		# 将常用命令参数设为默认,省去重复输入的工作
norecursedirs = .* build dist CVS _darcs {arch} *.egg src # 指定pytest忽略查找某些目录,多个目录之间空格隔开
log_cli = True			# 控制台实时输出日志

# 注册mark标记
markers =
	smoke : smoke tests as smoke
    output : print

Pytest标记

上面的配置文件中提到了标记,又如何使用呢?

默认情况下,pytest会递归查找当前目录下所有以test开头或结尾的Python脚本,并执行文件内所有以test开头的函数和方法。工作中由于功能尚未实现、挑选用例执行冒烟测试等原因,只想测试指定用例,在pytest中有几种方法可解决

  1. 显式指定函数名,通过::标记,例如:某文件内存在两个类,但一个类尚未完成,只想执行第二个类中的用例,就可指定类执行
  2. pytest -vs test_learn.py::Testpy2 # 只执行文件中的Testpy2类 pytest -vs test_learn.py::Testpy2::test_method # 只执行文件中Testpy2类下的test_method方法
  3. 使用模糊匹配,通过-k参数识别关键字,例如:
  4. pytest -k login test_learn.py # 只执行文件中包含关键字login的用例
  5. 使用pytest.mark在用例上进行标记,标记需要在pytest.ini文件中配置,配置方法见上文,格式一定要写对,若未配置也可执行,但是会出现警告,配置中冒号前是标记名,冒号后可以理解为标记解释,当然也可以前后一样
  6. @pytest.mark.check # 在函数上设置标记 def test_func1(self): assert 1 == 1 @pytest.mark.output # 在类上面设置标记,对类中所有方法都有效 class caseCls(object): def dyd_case(self): @pytest.mark.smoke def test_func1(self): # 在类中的方法上设置标记 print("这是Pytest")
  7. 执行带有标记的函数,使用-m参数
  8. pytest -m check test_learn.py # 执行带有check标记的用例,此标记在一个函数上标记了一次,所有只会执行一条用例 pytest -m smoke test_learn.py # 执行带有smoke标记的用例,此标记在一个方法上标记了一次,同样只会执行一条用例 pytest -m output test_learn.py # 执行带有output标记的用例,因为用例在类上,故类中所有的用例都会被执行

Pytest运行

  1. 通过命令行运行
  2. pytest -vs /package_name # 执行包中所有模块的用例 pytest -vs file_name.py # 执行单独的pytest模块,file_name.py文件 pytest -vs file_name.py::class_name # 只执行文件中的class_name类 pytest -vs file_name.py::class_name::method_name # 只执行文件中class_name类下的method_name方法 pytest -vs file_name.py::function_name # 只执行文件中function_name函数
  3. pytest命令参数
  • -v 展示每个测试函数的执行结果(详细信息)
  • -q 只显示整体测试结果(简要信息)
  • -s 展示测试函数中print()函数输出信息
  • -k 只执行包含关键字的用例
  • -m 只执行指定标记的用例
  • -x 出现失败用例则立即停止执行
  • -l 用例失败时打印相关局部变量
  • -c 从指定目录加载配置文件,而非自动查找配置文件
  • -lf 只执行上次失败的用例,若没有则执行全部用例
  • -ff 先执行完上次失败的再执行剩余的用例
  • -tb=style 用例失败时错误的详细程度(auto/long/short/line/native/no)
  • --maxfail=num 用例允许失败的最大次数,超出则立即停止执行
  • --collect-only 收集但不执行用例
  • --durations=num -vv 显示设定数值内,按照耗时时长降序打印结果,通常用于调优
  • -h--help 帮助
  • 通过main方法
  • if __name__ == '__main__': pytest.main(["-sv", "file_name.py"]) # 通过main函数执行

示例:

# 文件名称命名规则,以test_开头或_test结尾
import pytest
def test_func():	# 通常在类外面称为函数,函数命名规则,以test_开头
    assert 1 == 1
class Testpy(object):		# 类命名规则,以Test开头
    def test_method(self):	# 通常在类中称为方法,方法命名规则与函数一样,以test_开头
        assert 1 + 2 == 3
    @pytest.mark.smoke
    def test_func1(self):
        print("这是Pytest")
@pytest.mark.smoke
class caseCls(object):
    def dyd_case(self):
        print("修改函数后的规则后")
if __name__ == '__main__':
    pytest.main(["test_learn.py"])	# 通过main函数执行命令

使用命令执行示例中的用例

pytest ../Pytest								# 执行包中所有模块的用例
pytest -vs test_learn.py						# 执行单独的pytest模块,test_learn.py文件
pytest -vs test_learn.py::Testpy				# 只执行文件中的Testpy类
pytest -vs test_learn.py::Testpy::test_method	# 只执行文件中Testpy类下的test_method方法
pytest -vs test_learn.py::test_function			# 只执行文件中test_function函数
pytest -m smoke test_learn.py					# 执行带有smoke标记的用例
pytest -k dyd test_learn.py						# 只执行包含关键字dyd的用例
pytest --maxfail=2 test_learn.py				# 执行指定文件中的用例,失败用例超出2条则停止执行
pytest --collect-only test_learn.py				# 收集指定文件中的用例,不加文件名则直接搜索当前目录下所有用例



Pytest参数化

在pytest中,可以使用参数化测试,即每组参数都独立执行一次

使用参数化装饰器pytest.mark.parametrize(argnames,argvalues),参数化的名字要与方法中的参数名一一对应,传递多个参数时可使用列表、元组、字典或列表嵌套等,实际工作中还是以json、yaml、excel等文件形式做参数化的较多,后续再介绍,示例如下:

import pytest # 导入pytest包

list = [1,"1+1","1+2"] # 列表
# ↓参数必须与值的个数相等,可使用ids参数重命名,名字方便理解其意思即可,ids的个数要等于传递的数据个数
@pytest.mark.parametrize("value",list,ids=["value=1","value=2","value=3"])
def test_list(value):
    print(value)	# 打印三个结果

tuple = [("abc"),("def"),["g","h","i"],[1,2,3]] # 元组
@pytest.mark.parametrize("x,y,z",tuple)  # 参数必须与值的个数相等
def test_xzy(x,y,z):
    print(x,y,z)	# 打印四个结果

@pytest.mark.parametrize("input,expect",[("1+1",2),("2+2",4)]) 
def test_count(input,expect):
    assert eval(input) ==expect # 估算实际结果是否与期望结果一致,打印两个结果

dict = ({"username":"dyd","passwd":123456},{"phone":18888888888,"age":18}) # 字典
@pytest.mark.parametrize("dic",dict)
def test_dict(dic):
    print(dic)	# 打印两个结果

data = [ # ↓通过pytest中的param定义参数,id是对参数的一个说明,可自定义,方便理解各用例的含义即可
    pytest.param(100,100,200,id="a+b:pass"),
    pytest.param("a","b","ab",id="a+b:pass"),
    pytest.param(1,1,6,id="a+b:fail")]
def add(a,b): # 定义一个add相加的函数
    return a+b
class TestParam(object):
    @pytest.mark.parametrize("a,b,expect",data)
    def test_param(self,a,b,expect):
        assert add(a,b) == expect # 调用a+b,判断实际结果是否与期望结果一致,出现三个结果

@pytest.mark.parametrize("x",(1,2,3))
@pytest.mark.parametrize("y",(4,5,6))
def test_dkej(x,y):
    print(f"打印组合方式:({x},{y})") # 通过参数化实现笛卡尔积

Setup与teardown

在执行用例时,可能某些操作只需要进行一次,比如打开浏览器,或者每次操作前都需要先执行其它操作,比如查看数据前需要连接数据库,查看后需要断开数据库等,类似的操作可以使用以下几种setup/teardown进行配置与销毁

  • 模块级 setup_module/teardowm_module 开始于模块始末,全局的
  • 函数级 setup_function/teardown_function 只对函数用例生效(在类外)
  • 类级 setup_class/teardown_class 只在类中前后运行一次(在类中)
  • 方法级 setup_method/teardown_method 类中的每个方法执行前后(在类中)
  • 类中的 setup/teardown 运行在调用方法前后(常用)

下面是使用方法简单示例,setup/teardown并非必须成对使用,比如连接数据库后不想断开,就可以只使用setup

def setup_module():
    print("setup_模块级")
def teardown_module():
    print("teardown_模块级")
…… setup_function/teardown_function
def test_01():
    print("测试01")
def test_02():
    print("测试02")

class Testcase01(object):
    @classmethod
    def setup_class(cls):
        print("setup_类")
    def teardown_class(cls):
        print("teardown_类")
        …… setup_method/teardown_method
        …… setup/teardown
    def test_03(self):
        print("测试03")
    def test_04(self):
        print("测试04")

下面是两个函数test_01、02和两个方法test_03、04,在配置setup和teardown后运行的结果,从结果中可以看出它们的层级关系

setear_test.py::test_01 
setup_模块级
setup_函数
测试用例01	PASSED
teardown_函数

setear_test.py::test_02 
setup_函数
测试用例02	PASSED
teardown_函数

setear_test.py::Testcase01::test_03 
setup_类
setup_方法
setup
测试用例03	PASSED
teardown
teardown_方法

setear_test.py::Testcase01::test_04 
setup_方法
setup
测试用例04	PASSED
teardown
teardown_方法
teardown_类
teardown_模块级

Pytest fixture

上面setup/teardown可实现的功能,fixture可以更灵活的实现

定义fixture和定义普通函数差不多,唯一区别是在函数上加上个装饰器@pytest.fixture()

为了跟用例有所区分,fixture命名时不要以test开头,fixture是有返回值的,若没有返回值则默认为None

用例调用fixture的返回值,直接把fixture的函数名当作变量名

import pytest
@pytest.fixture()				# 默认范围为函数级(function)
def login():
    print("请先登录")
    return
def test_Search():				# 不需要登录,所以不调用fixture
    print("搜索商品,无需登录")
def test_Cart(login):			# 需要登录,调用fixture
    print("加购商品,需登录")

@pytest.fixture(scope="class")	# 设置范围为类级(class)
def login():
    print("请登录后再购买")
    return
class Testfix(object):
    def test_Look(self):
        print("查看商品,无需登录")	# 不需要登录,所以不调用fixture
    def test_Pay(self,login):
        print("购买商品,需登录")	# 需要登录,调用fixture

输出结果如下,控制范围(scope)还有module,package,session,以及其它参数如:param、ids、name等

test_fix.py::test_Search 
搜索商品,无需登录	PASSED
test_fix.py::test_Cart
请登录后再购买
加购商品,需登录	PASSED

test_fix.py::Testfix::test_Look
查看商品,无需登录	PASSED
test_fix.py::Testfix::test_Pay 
请登录后再购买
购买商品,需登录	PASSED

再介绍一下session级别,它是可以跨.py模块调用的,也就是说有多个.py文件的测试用例时,只需调用一次fixture,设置scope="session",并写在conftest.py文件中,conftest.py文件是固定名称,pytest会自动识别此文件,所以不可改名称,此文件放在项目根目录,则可全局调用,放在某个包中,则只对包内的有效

比如使用钩子函数实现失败用例截图,将以下代码写在conftest.py中,其它模块就可以直接调用

import allure
import pytest
from selenium import webdriver

driver = None # 自定义一个driver=None
@pytest.hookimpl(tryfirst=1,hookwrapper=1)
def pytest_runtest_makereport(item,call):   # 钩子函数
    # 用例执行完成后再执行此操作,(后置处理用yield)
    outcome = yield # 测试用例执行完后接下来要做什么事情
    rep = outcome.get_result() # 获取用例执行完成之后的结果
    if rep.when == "call" and rep.failed:   # 若结果正在被调用而且是失败的则进行截图
        img = driver.get_screenshot_as_png()  # 若出现异常,则进行截图操作
        allure.attach(img, "失败截图", allure.attachment_type.PNG)  # 将图片展现在Allure报告上

# 自定义一个Fixture,初始化driver对象
@pytest.fixture(scope="session",autouse=True)
def init_driver():
    global driver
    driver = webdriver.Chrome()
    return driver  # 返回初始化后的driver,就可以直接被调用啦



调用conftest.py中的函数

def test_baidu(init_driver):
    init_driver.get("http://baidu.com")
    init_driver.find_element(By.CLASS_NAME, "123").click()
    ……

Pytest Allure

Allure安装

Allure运行需要Java环境,Java安装并配置环境变量后,在命令窗口执行以下命令,更多信息可查阅官方文档

pip install allure-pytest

安装完成后,下载一个allure可执行文件,点击此次进入下载列表页,下载所需版本,下载后解压文件,然后将bin目录加到PATH环境变量中,最后在命令行窗口输入allure或者allure --version验证是否配置成功

Allure用例描述

添加装饰器

参数值

参数说明

@allure.epic()

epic描述

定义项目,往下是feature

@allure.feature()

模块名称

大的功能点描述,往下是story

@allure.story()

次级分类

用户故事,次级功能点描述,往下是title

@allure.title()

用例的标题

测试用例重命名

@allure.testcase()

测试用例的链接地址

对应功能测试用例系统里面的用例

@allure.issue()

测试用例的缺陷地址

对应缺陷管理系统里面的链接

@allure.description()

用例描述

测试用例的描述

@allure.step()

操作步骤

测试用例的步骤

@allure.severity()

用例等级

blocker,critical,normal,minor,trivial

@allure.link()

链接

定义一个链接,在测试报告展现

@allure.attach()

附件

报告添加附件,支持html、图片、视频

Allure报告生成

生成测试报告必须在命令行执行

  1. 生成中间结果,使用下面的命令指定结果生成到的目录,生成结果中包含两个格式文件(text、json)
  2. pytest --alluredir=./result --clean-alluredir # 将包中所有模块的用例生成报告并清除之前的报告 pytest --alluredir=./result test_Jpress.py # 将指定的文件生成报告 pytest --alluredir=./result test_Jpress.py::TestUser# 将指定文件中的类生成报告 …… # 可以指定方法或使用标记等生成报告
  3. 中间结果无法直接查看,执行下面命令启动Allure查看HTML报告
  4. allure serve ./result
  5. 也可以直接生成可在线访问的HTML结果
  6. allure generate ./result -o ./report --clean-alluredir # 可以加上--clean-alluredir参数,表示若指定目录已存在则先清空此目录 # 也可以加上--allure-no-capture,表示无需将pytest捕获的日志记录(logging)、标准输出(stdout)、标准错误(stderr)附加到报告中

示例如下:

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.wait import WebDriverWait
import pytest
import allure
from util import util   # 导入自己封装的日志模块

loginError = [["","654321","账号不能为空"],["test","123456","用户名不正确。"],
              ["admin","","密码不能为空"],["admin","654321","用户名或密码不正确"]]
loginOK = [["admin","123456","用户中心"]]

@allure.feature("Jpress后端测试")
class TestUser(object):
    def setup_class(self):
        self.driver = webdriver.Chrome()
        self.driver.maximize_window()
        self.driver.get("http://192.166.66.22:8080/user/login")
        self.logger = util.get_logger()
        self.logger.info("登录测试")

    @allure.story("这是登录失败的测试用例")
    @allure.description("登录平台-错误场景")
    @pytest.mark.parametrize("username,pwd,expected",loginError)
    def test_user_login_Error(self,username,pwd,expected):
        user = username
        pwd = pwd
        expected = expected

        # 清空输入框后输入用户名
        self.driver.find_element(By.NAME, "user").clear()
        self.driver.find_element(By.NAME, "user").send_keys(user)
        # 清空输入框后输入密码
        self.driver.find_element(By.NAME, "pwd").clear()
        self.driver.find_element(By.NAME, "pwd").send_keys(pwd)
        # 点击【登录】
        self.driver.find_element(By.CLASS_NAME, "btn").click()
        WebDriverWait(self.driver, 3).until(EC.alert_is_present())
        alert = self.driver.switch_to.alert
        # 验证报错信息是否正确
        assert alert.text == expected
        self.logger.debug("登录失败,用户名或密码输出有误!")
        alert.accept()

    @allure.story("后台管理平台登录失败")
    @allure.description("记录失败时的截图和日志")
    def test_failcase(self):
        self.driver = webdriver.Chrome()
        self.driver.get("http://192.166.66.22:8080/admin/login")
        self.driver.find_element(By.NAME, "user").send_keys("admin")
        self.driver.find_element(By.NAME, "pwd").send_keys("123456")
        self.driver.find_element(By.NAME, "captcha").send_keys(8888)
        try:
            self.driver.find_element(By.CLASS_NAME, "btn错误的登录按钮元素").click()
        except Exception as e:
            img = self.driver.get_screenshot_as_png()   # 若出现异常,则进行截图操作
            allure.attach(img, "失败截图", allure.attachment_type.PNG)  # 将图片展示在Allure报告上
            log = self.logger.error("报错信息:%s", "无法登录,找不到登录按钮", exc_info=1)  # 记录报错日志
            # 获取报错日志,并将日志展示在Allure报告上
            allure.attach.file(self.driver.get_log(log), "失败日志", allure.attachment_type.TEXT)
        WebDriverWait(self.driver, 3).until(EC.alert_is_present())
        alert = self.driver.switch_to.alert
        assert alert.text == "登录失败"

    @allure.story("这是登录成功用例")
    @allure.description("成功登录至平台")
    @allure.severity(severity_level="Blocker")
    @pytest.mark.parametrize("username,pwd,expected",loginOK)
    def test_user_login_OK(self,username,pwd,expected):
        user = username
        pwd = pwd
        expected = expected

        self.driver.find_element(By.NAME, "user").clear()
        self.driver.find_element(By.NAME, "user").send_keys(user)
        self.driver.find_element(By.NAME, "pwd").clear()
        self.driver.find_element(By.NAME, "pwd").send_keys(pwd)
        self.driver.find_element(By.CLASS_NAME, "btn").click()
        WebDriverWait(self.driver, 3).until(EC.title_is(expected))
        assert self.driver.title == expected

    @allure.story("文章管理测试用例")
    @allure.title("查看文章列表")
    @allure.step("步骤1:进入文章列表")
    def test_article_list(self):
        expected = "http://192.166.66.22:8080/ucenter/article"
        self.driver.find_element(By.XPATH,'//span[contains(text(),"我的文章")]').click()
        self.driver.find_element(By.LINK_TEXT,"文章列表").click()
        WebDriverWait(self.driver, 2).until(EC.url_contains("/ucenter/article"))
        assert self.driver.current_url == expected

    @allure.story("文章管理测试用例")
    @allure.title("查看文章详情")
    @allure.step("步骤2:查看文章详情")
    @allure.severity(severity_level="critical")
    def test_Look_article(self):
        expected = "一个小测试"
        handle = self.driver.current_window_handle  # 获取当前窗口句柄
        self.driver.find_element(By.LINK_TEXT, "一个小测试").click()
        handles = self.driver.window_handles  # 获取所有窗口句柄
        for newhandle in handles:  # 对窗口进行遍历
            if newhandle != handle:  # 判断当前窗口是否为新窗口
                self.driver.switch_to.window(newhandle)  # 切换到新打开的窗口
        WebDriverWait(self.driver, 2).until(EC.title_is(expected))
        assert self.driver.title == expected
        # 关闭浏览器
        self.driver.quit()

@allure.feature("Jpress前端测试")
class TestBlog(object):
    def setup_class(self):
        self.driver = webdriver.Chrome()
        self.driver.maximize_window()
        self.driver.get("http://192.166.66.22:8080/")
        self.logger = util.get_logger()
        self.logger.info("博客访问测试")

    @allure.title("查看博客详情")
    @pytest.mark.skipif(reason="刻意跳过此用例")
    def test_details(self):
        expected = "一个小测试"
        self.driver.find_element(By.CLASS_NAME,"bh-card-main-title").click()
        WebDriverWait(self.driver, 2).until(EC.url_contains("/article/2"))
        assert self.driver.title == expected
        # 关闭浏览器
        self.driver.quit()



下图是生成的Allure报告用例执行结果

数据驱动测试(DDT)

读取excel文件

需要安装xlrd模块,使用xlrd模块处理excel文件,新版xlrd不支持xlsx格式的excel,会报错xlrd.biffh.XLRDError: Excel xlsx file; not supported,可以使用xls格式的excel,或者可以安装老版本的xlrd

pip install xlrd==1.2.0	# 此版本支持xlx格式的excel

结合pytest的参数化的方式,实现excel文件数据驱动,示例如下:

import pytest
import xlrd

def get_data(): # 定义一个获取数据的函数
    filename = "my_data.xlsx" # 定义数据文件
    wb = xlrd.open_workbook(filename)   # 打开文件
    sheet = wb.sheet_by_index(0)    # 使用索引方式获得文件中的第一个sheet
    rows = sheet.nrows  # 获取行
    cols = sheet.ncols  # 获取列
    lst = []
    for row in range(rows): # 遍历行
        for col in range(cols): # 遍历列
            cell_data = sheet.cell_value(row,col)   # 获取单元格中的数据
            lst.append([cell_data])   # 将单元格中的数添加到列表中
    return lst  # 返回list列表

@pytest.mark.parametrize("value",get_data())    # 调用excel数据
def test_read(value):
    print(value)
    # 输出结果,2*2的表格,打印每个单元格中的数据
test_excel.py::test_read[value0] PASSED                                  [ 25%]['xlsx']
test_excel.py::test_read[value1] PASSED                                  [ 50%]['格式']
test_excel.py::test_read[value2] PASSED                                  [ 75%]['读取文件']
test_excel.py::test_read[value3] PASSED                                  [100%]['666']

读取csv文件

csv文件是使用逗号分隔的文本文件,直接使用python的csv模块处理csv文件,然后结合pytest的参数化的方式,实现csv文件数据驱动,

示例如下:

# 测,试
# ceshi,testing
import csv
import pytest

def get_data():
    with open("my_data.csv","r",encoding='UTF-8') as j:    # 只读模式打开csv文件
        data = csv.reader(j)    # csv.reader(f)返回一个csv_reader对象
        lst = []
        for row in data: # 遍历对象,每次返回一行
            lst.extend(row)   # 将数据一次性追加到lst中,将结果写到一个列表中
        return lst  # 返回lst

@pytest.mark.parametrize("csv_value",get_data())    # 调用csv数据
def test_read(csv_value):
    print(csv_value)    # 将写在列表中的数据一个一个的输出
    # 输出结果
test_csv.py::test_read[\u6d4b] PASSED                                    [ 25%]测
test_csv.py::test_read[\u8bd5] PASSED                                    [ 50%]试
test_csv.py::test_read[ceshi] PASSED                                     [ 75%]ceshi
test_csv.py::test_read[testing] PASSED                                   [100%]testing

读取json文件

直接使用python中的json模块处理json文件,然后结合pytest的参数化方式,实现json文件数据驱动

json文件内容

[
    {
        "name":"",
        "pwd":"654321",
        "expected":"账号不能为空"
    },
    {
        "name":"admin",
        "pwd":"",
        "expected":"密码不能为空"
    },
    {
        "name":"admin",
        "pwd":"654321",
        "expected":"用户名或密码不正确"
    }
]

示例如下:

import pytest
import json
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

def get_data():
    lst = []
    with open("my_data.json","r",encoding='UTF-8') as j:    # 只读模式打开json文件
        dict_data = json.loads(j.read())    # 将json转换为字典
        for i in dict_data: # 遍历字典
            lst.append(tuple(i.values()))   #将数据以元组形式添加到lst中
    return lst  # 返回lst

#	此时输出get_data()值,会得到的下面结果
[('', '654321', '账号不能为空'), ('admin', '', '密码不能为空'), ('admin', '654321', '用户名或密码不正确')]

class TestUser(object):
    def setup_class(self):
        self.driver = webdriver.Chrome()
        self.driver.maximize_window()
        self.driver.get("http://192.166.66.22:8080/user/login")

    @pytest.mark.parametrize("username,pwd,expected",get_data())    # 调用json数据
    def test_user_login_Error(self,username,pwd,expected):
        user = username
        pwd = pwd
        expected = expected

        # 清空输入框后输入用户名
        self.driver.find_element(By.NAME, "user").clear()
        self.driver.find_element(By.NAME, "user").send_keys(user)
        # 清空输入框后输入密码
        self.driver.find_element(By.NAME, "pwd").clear()
        self.driver.find_element(By.NAME, "pwd").send_keys(pwd)
        # 点击【登录】
        self.driver.find_element(By.CLASS_NAME, "btn").click()
        # 等待页面加载
        WebDriverWait(self.driver, 3).until(EC.alert_is_present())
        alert = self.driver.switch_to.alert
        # 验证报错信息是否正确
        assert alert.text == expected
        alert.accept()

读取yaml文件

先安装yaml模块,然后结合pytest的参数化方式,实现yaml文件数据驱动,和json差不多

pip install pyyaml

yaml文件内容

---
name: ""
pwd: "123456"
expected: "账号不能为空"
---
name: "admin"
pwd: ""
expected: "密码不能为空"
---
name: "admin"
pwd: "654321"
expected: "用户名或密码不正确"

使用yaml验证登录,示例如下:

import pytest
import yaml
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

def get_data():
    lst = []
    with open("my_data.yaml","r",encoding='UTF-8') as j:    # 只读模式打开yaml文件
        ym = yaml.load_all(j.read()) # 使用load_all生成迭代器
        for i in ym: # 遍历ym中的数据
            lst.append(tuple(i.values()))   #将数据以元组形式添加到lst中
    return lst  # 返回lst

#	此时输出get_data()值,会得到和读取json文件一样的结果
[('', '123456', '账号不能为空'), ('admin', '', '密码不能为空'), ('admin', '654321', '用户名或密码不正确')]

class TestUser(object):
    def setup_class(self):
        self.driver = webdriver.Chrome()
        self.driver.maximize_window()
        self.driver.get("http://192.166.66.22:8080/user/login")

    @pytest.mark.parametrize("username,pwd,expected",get_data())    # 调用yaml数据
    def test_user_login_Error(self,username,pwd,expected):
        user = username
        pwd = pwd
        expected = expected

        # 清空输入框后输入用户名
        self.driver.find_element(By.NAME, "user").clear()
        self.driver.find_element(By.NAME, "user").send_keys(user)
        # 清空输入框后输入密码
        self.driver.find_element(By.NAME, "pwd").clear()
        self.driver.find_element(By.NAME, "pwd").send_keys(pwd)
        # 点击【登录】
        self.driver.find_element(By.CLASS_NAME, "btn").click()
        # 等待页面加载
        WebDriverWait(self.driver, 3).until(EC.alert_is_present())
        alert = self.driver.switch_to.alert
        # 验证报错信息是否正确
        assert alert.text == expected
        alert.accept()

Jenkins集成Allure报告

本次Jenkins是安装在Windows上的,直接Jenkins官网下载安装包进行安装就行啦,不多做介绍

  1. Jenkins安装完成后,进入插件管理,安装Allure插件


2.进入全局配置,配置Allure Commandline,填写本地Allure安装路径,别名自定义,完成后点击【应用】→【保存】

3.新建一个自由风格的项目

4.配置项目,在常规配置里点击【高级】,勾选“使用自定义的工作空间”,填写工作空间目录(通常填写项目的主目录),显示名称自定义

然后增加构建步骤,选择“执行windows批处理命令”,填写要执行的命令,命令可以指定文件或类等,具体上文有介绍

pytest -vs --alluredir=./pytest_result test_Jpress.py --clean-alluredir

最后增加构建后操作步骤,选择“Allure Report”,填写结果路径,一定要与构建时的路径相同,然后【应用】→【保存】

8.在Jenkins页面上会出现Allure报告标识,点击可查看具体构建报告

报告结果如下图所示

好了今天的分享就到这了,喜欢的记得关注点赞哟