This commit is contained in:
yanchunhuo 2019-09-18 12:41:17 +08:00
parent b30794fe8e
commit 7552a590ca
99 changed files with 766 additions and 157 deletions

182
README.md
View File

@ -3,20 +3,28 @@
# [API自动化测试]()
# [概况]()
* 本项目由pytest、assertpy、requests、PyMySQL、allure、JPype1组成
* pytest是python的一个单元测试框架,https://docs.pytest.org/en/latest/
* assertpy是一个包含丰富的断言库支持pytest,https://github.com/ActivisionGameScience/assertpy
* requests是http请求框架,http://docs.python-requests.org/en/master/
* PyMySQL用于操作MySQL数据库,https://github.com/PyMySQL/PyMySQL
* allure用于生成测试报告,http://allure.qatools.ru/
* JPype1用于执行java代码,https://github.com/jpype-project/jpype
* 本项目由以下工具组成
* pytestpython的一个单元测试框架,https://docs.pytest.org/en/latest/
* pytest-xdistpytest的一个插件,可多进程同时执行测试用例,https://github.com/pytest-dev/pytest-xdist
* allure-pytest用于生成测试报告,http://allure.qatools.ru/
* assertpy一个包含丰富的断言库支持pytest,https://github.com/ActivisionGameScience/assertpy
* requestshttp请求框架,http://docs.python-requests.org/en/master/
* Appium-Python-Client移动端的自动化测试框架,https://github.com/appium/appium/tree/v1.9.1
* seleniumweb ui自动化测试框架,https://www.seleniumhq.org/
* cx_Oracleoracle操作库,https://cx-oracle.readthedocs.io/en/latest/index.html
* JPype1用于执行java代码,https://github.com/jpype-project/jpype
* paramikossh客户端,http://docs.paramiko.org/en/2.4/
* Pillow用于图片处理,https://pillow.readthedocs.io/en/latest/
* PyMySQL用于操作MySQL数据库,https://github.com/PyMySQL/PyMySQL
* redisredis客户端,https://pypi.org/project/redis/
# [使用]()
## 一、环境准备
### 1、安装python依赖模块
### 1、脚本运行环境准备
#### 1.1、安装python依赖模块
* pip3 install -r requirements.txt
### 2、安装allure
#### 1.2、安装allure
* 源安装
* sudo apt-add-repository ppa:qameta/allure
* sudo apt-get update
@ -27,41 +35,171 @@
* 解压allure-2.7.0.zip
* 加入系统环境变量:export PATH=/home/john/allure-2.7.0/bin:$PATH
### 3、安装openjdk8或jdk8
#### 1.3、安装openjdk8或jdk8
* sudo add-apt-repository ppa:openjdk-r/ppa
* sudo apt-get update
* sudo apt-get install openjdk-8-jdk
#### 1.4、安装Oracle Instant Client
* linux
* 安装libaio包
* centos:yum install libaio
* ubuntu:apt-get install libaio1
* 配置Oracle Instant Client
* 下载地址:http://www.oracle.com/technetwork/topics/linuxx86-64soft-092277.html
* 下载安装包instantclient-basic-linux.x64-18.3.0.0.0dbru.zip
* 解压zip包,并配置/etc/profile
* unzip instantclient-basic-linux.x64-18.3.0.0.0dbru.zip
* export LD_LIBRARY_PATH=/home/john/oracle_instant_client/instantclient_18_3:$LD_LIBRARY_PATH
* 中文编码设置
```python
import os
os.environ['NLS_LANG'] = 'SIMPLIFIED CHINESE_CHINA.UTF8'
```
* Windows
* 下载地址:http://www.oracle.com/technetwork/topics/winx64soft-089540.html
* 下载安装包instantclient-basic-windows.x64-11.2.0.4.0.zip
* 解压zip包,并配置环境变量
* 系统环境变量加入D:\instantclient-basic-windows.x64-11.2.0.4.0\instantclient_11_2
* 配置中文编码,环境变量创建NLS_LANG=SIMPLIFIED CHINESE_CHINA.UTF8
* 注意:如果使用64位,python和instantclient都需要使用64位
#### 1.5、图像识别字库准备
* 下载对应字库:https://github.com/tesseract-ocr/tessdata
* 将下载的字库放到common/java/lib/tess4j/tessdata/
### 2、selenium server运行环境准备
#### 2.1、安装jdk1.8,并配置环境变量
* export JAVA_HOME=/usr/lib/jvm/jdk8
* export JRE_HOME=${JAVA_HOME}/jre
* export CLASSPATH=.:${JAVA_HOME}/lib:${JRE_HOME}/lib
* export PATH=${JAVA_HOME}/bin:$PATH
#### 2.2、安装配置selenium
* 配置selenium server
* 下载selenium-server-standalone-3.14.0.jar
* 下载地址:http://selenium-release.storage.googleapis.com/index.html
* 以管理员身份启动服务:java -jar selenium-server-standalone-3.14.0.jar -log selenium.log
* 下载浏览器驱动
* 谷歌浏览器https://chromedriver.storage.googleapis.com/index.html
* 火狐浏览器https://github.com/mozilla/geckodriver/
* 驱动支持的浏览器版本https://firefox-source-docs.mozilla.org/testing/geckodriver/geckodriver/Support.html
* IE浏览器(建议使用32位,64位操作极慢)http://selenium-release.storage.googleapis.com/index.html
* 将驱动所在目录加入到selenium server服务器系统环境变量:export PATH=/home/john/selenium/:$PATH
* IE浏览器设置
* 在Windows Vista、Windows7系统上的IE浏览器在IE7及以上版本中需要设置四个区域的保护模式为一样设置开启或者关闭都可以。
* 工具-->Internet选项-->安全
* IE10及以上版本增强保护模式需要关闭。
* 工具-->Internet选项-->高级
* 浏览器缩放级别必须设置为100%,以便本地鼠标事件可以设置为正确的坐标。
* 针对IE11需要设置注册表以便于浏览器驱动与浏览器建立连接
* Windows 64位HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Internet Explorer\Main\FeatureControl\FEATURE_BFCACHE
* Windows 32位HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Internet Explorer\Main\FeatureControl\FEATURE_BFCACHE
* 如果FEATRUE_BFCACHE项不存在需要创建一个然后在里面创建一个DWORD(32位)命名为iexplore.exe值为0
* Windows 64位两个注册表建议都设置
* IE8及以上版本设置支持inprivate模式以便多开IE窗口时cookies能够独享
* HKKY_CURRENT_USER\Software\Microsoft\Internet Explorer\Main 下建一个名为TabProcGrowth的DWORD(32位)值为0
* 重启系统
* 注:https://github.com/SeleniumHQ/selenium/wiki/InternetExplorerDriver#required-configuration
### 3、appium server运行环境准备
#### 3.1、安装jdk1.8,并配置环境变量
* export JAVA_HOME=/usr/lib/jvm/jdk8
* export JRE_HOME=${JAVA_HOME}/jre
* export CLASSPATH=.:${JAVA_HOME}/lib:${JRE_HOME}/lib
* export PATH=${JAVA_HOME}/bin:$PATH
#### 3.2、安装配置appium server
* 安装appium desktop server
* 下载appium-desktop-setup-1.8.2.exe
* 下载地址:https://github.com/appium/appium-desktop/releases
* 以管理员身份启动服务
* Android环境准备
* 安装java(JDK),并配置JAVA_HOME=/usr/lib/jvm/jdk8
* 安装Android SDK,并配置ANDROID_HOME="/usr/local/adt/sdk"
* 使用SDK manager安装需要进行自动化的Android API版本
* 手机chrome环境准备
* 确保手机已安装chrome浏览器
* 下载chrome浏览器驱动https://chromedriver.storage.googleapis.com/index.html
* 驱动支持的手机浏览器版本https://github.com/appium/appium/blob/master/docs/en/writing-running-appium/web/chromedriver.md
* 在appium desktop上设置驱动的路径
* Windows环境准备
* 支持Windows10及以上版本
* 设置Windows处于开发者模式
* 下载WinAppDriver并安装(V1.1版本),https://github.com/Microsoft/WinAppDriver/releases
* \[可选\]下载安装WindowsSDK,在Windows Kits\10\bin\10.0.17763.0\x64内包含有inspect.exe用于定位Windows程序的元素信息
* 其他更多配置https://github.com/appium/appium/tree/v1.9.1/docs/en/drivers
## 二、修改配置
* vim config/init.ini 配置需要初始化的项目
* vim config/projectName.ini 配置测试项目的信息
* vim config/app_ui_config.conf 配置app ui自动化的测试信息
* vim config/web_ui_config.conf 配置web ui自动化的测试信息
* vim config/projectName/projectName.conf 配置测试项目的信息
## 三、运行测试
* cd APIAutomationTest/
* python3 runTest.py --help
* python3 runTest.py 运行cases目录所有的用例
* python3 runTest.py -k keyword 运行匹配关键字的用例,会匹配文件名、类名、方法名
* python3 runTest.py -d dir 运行指定目录的用例默认运行cases目录
### 1、API测试
* cd AutomationTest/
* python3 run_api_test.py --help
* python3 run_api_test.py 运行cases目录所有的用例
* python3 run_api_test.py -k keyword 运行匹配关键字的用例,会匹配文件名、类名、方法名
* python3 run_api_test.py -d dir 运行指定目录的用例默认运行cases目录
### 2、web ui测试
* cd AutomationTest/
* python3 run_web_ui_test.py --help
* python3 run_web_ui_test.py 运行cases目录所有的用例
* python3 run_web_ui_test.py -k keyword 运行匹配关键字的用例,会匹配文件名、类名、方法名
* python3 run_web_ui_test.py -d dir 运行指定目录的用例默认运行cases目录
### 3、app ui测试
* cd AutomationTest/
* python3 run_app_ui_test.py --help
* python3 run_app_ui_test.py 运行cases目录所有的用例
* python3 run_app_ui_test.py -k keyword 运行匹配关键字的用例,会匹配文件名、类名、方法名
* python3 run_app_ui_test.py -d dir 运行指定目录的用例默认运行cases目录
## 四、生成测试报告
* cd APIAutomationTest/
* python3 generateReport.py -p 9080
### 1、API测试
* cd AutomationTest/
* python3 generate_api_test_report.py -p 9080
* 访问地址http://ip:9080
* 在使用Ubuntu进行报告生成时请勿使用sudo权限否则无法生成allure不支持
### 2、web ui测试
* cd AutomationTest/
* python3 generateReport_web_ui_test_report.py -ieport 9081 -chromeport 9082 -firefoxport 9083
* 访问地址http://ip:908[1-3]
* 在使用Ubuntu进行报告生成时请勿使用sudo权限否则无法生成allure不支持
### 3、app ui测试
* cd AutomationTest/
* python3 generateReport_app_ui_test_report.py -p 9084
* 访问地址http://ip:9084
### 注在使用Ubuntu进行报告生成时请勿使用sudo权限否则无法生成allure不支持
# [项目结构]()
* base 基础请求类
* cases 测试用例目录
* common 公共模块
* common_projects 每个项目的公共模块
* config 配置文件
* init 初始化
* logs 日志目录
* output 测试结果输出目录
* packages app ui测试的安装包
* pojo 存放自定义类对象
* test_data 测试所需的测试数据目录
* runTest.py 测试运行脚本
* generateReport.py 报告生成脚本
* run_api_test.py 运行api测试脚本
* run_web_ui_test.py 运行web ui测试脚本
* run_app_ui_test.py 运行app ui测试脚本
* generate_api_test_report.py 生成api测试报告
* generateReport_web_ui_test_report.py 生成web ui测试报告
* generateReport_app_ui_test_report.py 生成app ui测试报告
# [编码规范]()
* 统一使用python 3.6

View File

@ -1,10 +1,10 @@
# -*- coding:utf8 -*-
from base.demoProject.demoProjectReadConfig import DemoProjectReadConfig
from base.demoProject.demoProjectDBClients import DemoProjectDBClients
from base.api.demoProject.api_demoProject_read_config import API_DemoProject_Read_Config
from base.api.demoProject.api_demoProject_db_clients import API_DemoProject_DB_Clients
from common.httpclient.doRequest import DoRequest
from common.strTool import StrTool
class DemoProjectClient(object):
class API_DemoProject_Client(object):
__instance=None
__inited=None
@ -15,8 +15,8 @@ class DemoProjectClient(object):
def __init__(self):
if self.__inited is None:
self.demoProjectConfig=DemoProjectReadConfig().config
self.demoProjectDBClients=DemoProjectDBClients()
self.demoProjectConfig=API_DemoProject_Read_Config().config
self.demoProjectDBClients=API_DemoProject_DB_Clients()
self.doRequest=DoRequest(self.demoProjectConfig.url)
self.csrftoken=self._initCsrftoken()

View File

@ -1,8 +1,8 @@
# -*- coding:utf-8 -*-
from base.demoProject.demoProjectReadConfig import DemoProjectReadConfig
from base.api.demoProject.api_demoProject_read_config import API_DemoProject_Read_Config
class DemoProjectDBClients(object):
class API_DemoProject_DB_Clients(object):
__instance=None
__inited=None
@ -13,7 +13,7 @@ class DemoProjectDBClients(object):
def __init__(self):
if self.__inited is None:
self._demoProjectConfig = DemoProjectReadConfig().config
self._demoProjectConfig = API_DemoProject_Read_Config().config
# self.mysqlclient=MysqlClient('host','port','username','password','dbname')
# self.oracleclient = OracleClient('host', 'port', 'username', 'password', 'dbname')
self.__inited=True

View File

@ -1,8 +1,8 @@
# -*- coding:utf-8 -*-
from pojo.demoProjectConfig import DemoProjectConfig
from pojo.api.demoProjectConfig import DemoProjectConfig
import configparser as ConfigParser
class DemoProjectReadConfig(object):
class API_DemoProject_Read_Config(object):
__instance=None
__inited=None
@ -13,7 +13,7 @@ class DemoProjectReadConfig(object):
def __init__(self):
if self.__inited is None:
self.config=self._readConfig('config/demoProjectInit.conf')
self.config=self._readConfig('config/demoProject/api_demoProject.conf')
self.__inited=True
def _readConfig(self,configFile):
@ -27,4 +27,5 @@ class DemoProjectReadConfig(object):
demoProjectConfig.normalUserPassword=config.get('users','normalUserPassword')
demoProjectConfig.closeUser=config.get('users','closeUser')
demoProjectConfig.closeUserPassword=config.get('users','closeUserPassword')
demoProjectConfig.init=config.get('isInit','init')
return demoProjectConfig

View File

@ -0,0 +1,23 @@
# -*- coding:utf-8 -*-
from appium import webdriver
from base.read_app_ui_config import Read_APP_UI_Config
from base.app_ui.android.demoProject.app_ui_android_demoProject_read_config import APP_UI_Android_DemoProject_Read_Config
from common.appium.appOperator import AppOperator
class APP_UI_Android_demoProject_Client(object):
__instance=None
__inited=None
def __new__(cls, *args, **kwargs):
if cls.__instance is None:
cls.__instance=object.__new__(cls)
return cls.__instance
def __init__(self):
if self.__inited is None:
self.config = Read_APP_UI_Config().app_ui_config
self.demoProjectConfig = APP_UI_Android_DemoProject_Read_Config().config
self.driver = webdriver.Remote(self.config.appium_hub,desired_capabilities=self.demoProjectConfig.get_desired_capabilities())
self.appPerator = AppOperator(self.driver)
self.__inited=True

View File

@ -0,0 +1,35 @@
# -*- coding:utf-8 -*-
from pojo.app_ui.android.demoProject.demoProjectConfig import DemoProjectConfig
import configparser as ConfigParser
import os
class APP_UI_Android_DemoProject_Read_Config(object):
__instance=None
__inited=None
def __new__(cls, *args, **kwargs):
if cls.__instance is None:
cls.__instance=object.__new__(cls)
return cls.__instance
def __init__(self):
if self.__inited is None:
self.config=self._readConfig('config/demoProject/app_ui_android_demoProject.conf')
self.__inited=True
def _readConfig(self, configFile):
config = ConfigParser.ConfigParser()
config.read(configFile,encoding='utf-8')
demoProjectConfig = DemoProjectConfig()
demoProjectConfig.platformName = config.get('desired_capabilities','platformName')
demoProjectConfig.automationName = config.get('desired_capabilities','automationName')
demoProjectConfig.platformVersion = config.get('desired_capabilities','platformVersion')
demoProjectConfig.deviceName = config.get('desired_capabilities','deviceName')
demoProjectConfig.appActivity = config.get('desired_capabilities','appActivity')
demoProjectConfig.appPackage = config.get('desired_capabilities','appPackage')
demoProjectConfig.app = config.get('desired_capabilities','app')
demoProjectConfig.init = config.get('isInit','init')
# 将安装包所在位置转为绝对路径
if demoProjectConfig.app:
demoProjectConfig.app = os.path.abspath(demoProjectConfig.app)
return demoProjectConfig

View File

View File

View File

View File

@ -0,0 +1,20 @@
# -*- coding:utf-8 -*-
from base.web_ui.demoProject.web_ui_demoProject_read_config import WEB_UI_DemoProject_Read_Config
from base.read_web_ui_config import Read_WEB_UI_Config
from common.selenium.browserOperator import BrowserOperator
from common.selenium.driverTool import DriverTool
from page_objects.web_ui.demoProject.pages.loginPage import LoginPage
class WEB_UI_DemoProject_Client:
def __init__(self,browserOperator_type=0):
"""
:param browserOperator_type:0-未登录1-已登录
"""
self.config=Read_WEB_UI_Config().web_ui_config
self.demoProjectConfig=WEB_UI_DemoProject_Read_Config().config
self.driver = DriverTool.get_driver(self.config.selenium_hub, self.config.current_browser)
self.driver.get(self.demoProjectConfig.web_host + '/cloud/auth/login/')
self.browserOperator = BrowserOperator(self.driver)
if browserOperator_type==1:
loginPage=LoginPage(self.browserOperator)
loginPage.login_success(self.demoProjectConfig.normal_username, self.demoProjectConfig.normal_password)

View File

@ -0,0 +1,31 @@
# -*- coding:utf-8 -*-
from pojo.web_ui.demoProject.demoProjectConfig import DemoProjectConfig
import configparser as ConfigParser
class WEB_UI_DemoProject_Read_Config(object):
__instance=None
__inited=None
def __new__(cls, *args, **kwargs):
if cls.__instance is None:
cls.__instance=object.__new__(cls)
return cls.__instance
def __init__(self):
if self.__inited is None:
self.config=self._readConfig('config/demoProject/web_ui_demoProject.conf')
self.__inited=True
def _readConfig(self, configFile):
config = ConfigParser.ConfigParser()
config.read(configFile)
demoProjectConfig = DemoProjectConfig()
demoProjectConfig.web_host = config.get('servers','web_host')
demoProjectConfig.mysql_server = config.get('mysql', 'mysql_server')
demoProjectConfig.mysql_port = config.get('mysql', 'mysql_port')
demoProjectConfig.mysql_username = config.get('mysql', 'mysql_username')
demoProjectConfig.mysql_password = config.get('mysql', 'mysql_password')
demoProjectConfig.normal_username = config.get('users','normal_username')
demoProjectConfig.normal_password = config.get('users', 'normal_password')
demoProjectConfig.init=config.get('isInit','init')
return demoProjectConfig

View File

View File

View File

@ -1,12 +1,12 @@
#-*- coding:utf8 -*-
from assertpy import assert_that
from base.demoProject.demoProjectClient import DemoProjectClient
from base.api.demoProject.api_demoProject_client import API_DemoProject_Client
import pytest
class TestLogin:
def setup_class(self):
self._demoProjectClient=DemoProjectClient()
self._api_demoProject_client=API_DemoProject_Client()
self._login_path='/horizon/auth/login/'
@pytest.fixture
@ -27,16 +27,16 @@ class TestLogin:
return params
def test_success_login(self,fixture_test_success_login):
params=self.generateParams(self._demoProjectClient.csrftoken,'admin','admin','admin','admin')
httpResponseResult=self._demoProjectClient.doRequest.post_with_form(self._login_path,params=params)
params=self.generateParams(self._api_demoProject_client.csrftoken,'admin','admin','admin','admin')
httpResponseResult=self._api_demoProject_client.doRequest.post_with_form(self._login_path,params=params)
status_code=httpResponseResult.status_code
body=httpResponseResult.body
assert_that(status_code).is_equal_to(200)
assert_that(body).contains('admin')
def test_fail_login(self):
params=self.generateParams(self._demoProjectClient.csrftoken,'admin','admin1','admin','admin1')
httpResponseResult=self._demoProjectClient.doRequest.post_with_form(self._login_path,params=params)
params=self.generateParams(self._api_demoProject_client.csrftoken,'admin','admin1','admin','admin1')
httpResponseResult=self._api_demoProject_client.doRequest.post_with_form(self._login_path,params=params)
status_code=httpResponseResult.status_code
body=httpResponseResult.body
assert_that(status_code).is_equal_to(403)
@ -44,8 +44,8 @@ class TestLogin:
@pytest.mark.skip(reason=u'空密码前端做校验')
def test_empty_password_login(self):
params=self.generateParams(self._demoProjectClient.csrftoken,'admin','','admin','')
httpResponseResult=self._demoProjectClient.doRequest.post_with_form(self._login_path,params=params)
params=self.generateParams(self._api_demoProject_client.csrftoken,'admin','','admin','')
httpResponseResult=self._api_demoProject_client.doRequest.post_with_form(self._login_path,params=params)
status_code=httpResponseResult.status_code
body=httpResponseResult.body
assert_that(status_code).is_equal_to(403)
@ -54,8 +54,8 @@ class TestLogin:
fail_data=[('11111','1111'),('admin',''),('',''),(u'中文用户名',u'中文密码')]
@pytest.mark.parametrize('username,password',fail_data)
def test_with_params_fail_login(self,username,password):
params = self.generateParams(self._demoProjectClient.csrftoken, username, password, username, password)
httpResponseResult = self._demoProjectClient.doRequest.post_with_form(self._login_path, params=params)
params = self.generateParams(self._api_demoProject_client.csrftoken, username, password, username, password)
httpResponseResult = self._api_demoProject_client.doRequest.post_with_form(self._login_path, params=params)
status_code = httpResponseResult.status_code
body=httpResponseResult.body
assert_that(status_code).is_equal_to(403)

View File

View File

@ -0,0 +1,35 @@
# -*- coding:utf8 -*-
from base.app_ui.android.demoProject.app_ui_android_demoProject_client import APP_UI_Android_demoProject_Client
from page_objects.app_ui.android.demoProject.pages.startPage import StartPage
import pytest
import time
class TestIndexPage:
def setup_class(self):
self.demoProjectClient = APP_UI_Android_demoProject_Client()
self.startPage=StartPage(self.demoProjectClient.appPerator)
self.startPage.click_start()
self.indexPage=self.startPage.choice_a_city()
self.appPerator=self.demoProjectClient.appPerator
@pytest.fixture(autouse=True)
def record_test_case_video(self):
self.demoProjectClient.appPerator.start_recording_screen()
yield self.record_test_case_video
self.demoProjectClient.appPerator.stop_recording_screen()
@pytest.fixture
def fixture_test_silde(self):
print('start......')
yield self.fixture_test_silde
print('end......')
def test_silde(self,fixture_test_silde):
time.sleep(10)
self.indexPage.index_left_silde()
self.indexPage.index_right_silde()
self.indexPage.index_up_silde()
self.indexPage.index_down_silde()
def teardown_class(self):
self.demoProjectClient.appPerator.reset_app()

View File

@ -0,0 +1,35 @@
# -*- coding:utf8 -*-
from base.app_ui.android.demoProject.app_ui_android_demoProject_client import APP_UI_Android_demoProject_Client
from page_objects.app_ui.android.demoProject.pages.startPage import StartPage
import pytest
class TestStartPage:
def setup_class(self):
self.demoProjectClient = APP_UI_Android_demoProject_Client()
self.startPage=StartPage(self.demoProjectClient.appPerator)
@pytest.fixture(autouse=True)
def record_test_case_video(self):
self.demoProjectClient.appPerator.start_recording_screen()
yield self.record_test_case_video
self.demoProjectClient.appPerator.stop_recording_screen()
@pytest.fixture
def fixture_test_click_start_btn(self):
print('start......')
yield self.fixture_test_click_start_btn
print('end......')
def test_click_start_btn(self,fixture_test_click_start_btn):
self.startPage.click_start()
def test_search_chinese(self):
self.startPage.searh_city('大学')
def test_search_en(self):
self.startPage.searh_city('aaa')
def test_choice_a_city(self):
self.startPage.choice_a_city()
def teardown_class(self):
self.demoProjectClient.appPerator.reset_app()

View File

View File

View File

View File

View File

View File

@ -0,0 +1,44 @@
# -*- coding:utf8 -*-
from assertpy import assert_that
from base.web_ui.demoProject.web_ui_demoProject_client import WEB_UI_DemoProject_Client
from page_objects.web_ui.demoProject.pages.loginPage import LoginPage
import pytest
class TestLogin:
def setup_class(self):
self.demoProjectClient=WEB_UI_DemoProject_Client()
self.loginPage=LoginPage(self.demoProjectClient.browserOperator)
@pytest.fixture
def fixture_test_login_success(self):
yield self.fixture_test_login_success
self.indexPage.click_user()
self.indexPage.click_user_logout()
def test_login_success(self,fixture_test_login_success):
self.indexPage=self.loginPage.login_success(self.demoProjectClient.demoProjectConfig.normal_username,self.demoProjectClient.demoProjectConfig.normal_password)
assert_that(self.demoProjectClient.browserOperator.getTitle()).is_equal_to(self.indexPage.getElements().title.wait_expected_value)
def test_empty_username_and_empty_password(self):
self.loginPage.login_fail('', '')
assert_that(self.demoProjectClient.browserOperator.getText(
self.loginPage.getElements().loginEmptyUsernameAndPassword_password_tip)).is_equal_to(
self.loginPage.getElements().loginEmptyUsernameAndPassword_password_tip.expected_value)
assert_that(self.demoProjectClient.browserOperator.getText(
self.loginPage.getElements().loginEmptyUsernameAndPassword_username_tip)).is_equal_to(
self.loginPage.getElements().loginEmptyUsernameAndPassword_username_tip.expected_value)
def test_empty_username(self):
self.loginPage.login_fail('','123456')
assert_that(self.demoProjectClient.browserOperator.getText(
self.loginPage.getElements().loginEmptyUsername_tip)).is_equal_to(
self.loginPage.getElements().loginEmptyUsername_tip.expected_value)
def test_empty_password(self):
self.loginPage.login_fail('admin', '')
assert_that(self.demoProjectClient.browserOperator.getText(
self.loginPage.getElements().loginEmptyPassword_tip)).is_equal_to(
self.loginPage.getElements().loginEmptyPassword_tip.expected_value)
def teardown_class(self):
self.demoProjectClient.browserOperator.close()

View File

@ -0,0 +1,19 @@
#-*- coding:utf-8 -*-
from base.web_ui.demoProject.web_ui_demoProject_client import WEB_UI_DemoProject_Client
from page_objects.web_ui.demoProject.pages.indexPage import IndexPage
from assertpy import assert_that
class TestNetworkSecgroup:
def setup_class(self):
self.demoProject=WEB_UI_DemoProject_Client(1)
# 进入到安全组页面
indexPage=IndexPage(self.demoProject.browserOperator)
indexPage.click_menu_network()
self.secgroupPage=indexPage.click_menu_network_secgroup()
def test_secgroup_search_with_chinese_chars(self):
self.secgroupPage.search_secgroup('中文啊啊啊')
assert_that(2).is_equal_to(2)
def teardown_class(self):
self.demoProject.browserOperator.close()

View File

@ -0,0 +1,14 @@
# -*- coding:utf8 -*-
from base.web_ui.demoProject.web_ui_demoProject_client import WEB_UI_DemoProject_Client
from page_objects.web_ui.demoProject.pages.indexPage import IndexPage
class TestIndex:
def setup_class(self):
self.demoProjectClient = WEB_UI_DemoProject_Client(1)
self.indexPage=IndexPage(self.demoProjectClient.browserOperator)
def test_click_secgroup_menu(self):
self.indexPage.click_menu_network()
self.indexPage.click_menu_network_secgroup()
def teardown_class(self):
self.demoProjectClient.browserOperator.close()

View File

@ -6,8 +6,8 @@ from base.read_app_ui_config import Read_APP_UI_Config
from common.dateTimeTool import DateTimeTool
from common.httpclient.doRequest import DoRequest
from common.captchaRecognitionTool import CaptchaRecognitionTool
from page_objects.web_ui.locator_type import Locator_Type
from page_objects.web_ui.wait_type import Wait_Type as Wait_By
from page_objects.app_ui.locator_type import Locator_Type
from page_objects.app_ui.wait_type import Wait_Type as Wait_By
from pojo.elementInfo import ElementInfo
from PIL import Image
from selenium.common.exceptions import StaleElementReferenceException

View File

View File

@ -12,3 +12,8 @@ normalUserPassword=baiwu
closeUser=test11
closeUserPassword=test11
[isInit]
# 项目名=1 进行初始化
# 项目名=0 不进行初始化
init = 1

View File

@ -0,0 +1,18 @@
[desired_capabilities]
platformName = Android
# andorid4.2及以上使用的是UiAutomator和UiAutomator2
# android5.0及以上才能使用UiAutomator2
# 如果要使用UiAutomator2,则设置automationName=UIAutomator2,不使用这放空
automationName = UiAutomator2
platformVersion = 5.0
deviceName = Android Emulator
# appActivity、appPackage和app(安装包路径)两者选一,不选的放空
# 查看当前应用的的appActivity、appPackage
appPackage = com.moji.mjweather
appActivity = com.moji.mjweather.MainActivity
app =
[isInit]
# 项目名=1 进行初始化
# 项目名=0 不进行初始化
init = 1

View File

@ -0,0 +1,17 @@
[servers]
web_host = http://192.168.59.122
[mysql]
mysql_server = 127.0.0.1
mysql_port = 3306
mysql_username = root
mysql_password = root
[users]
normal_username = zpyadmin
normal_password = 123456..
[isInit]
# 项目名=1 进行初始化
# 项目名=0 不进行初始化
init = 1

View File

@ -1,4 +0,0 @@
[isInit]
#项目名=1 进行初始化
#项目名=0 不进行初始化
demoProject=1

View File

@ -0,0 +1,84 @@
#-*- coding:utf8 -*-
import argparse
import subprocess
import sys
if __name__=='__main__':
parser=argparse.ArgumentParser()
parser.add_argument('-ieport','--ieport',help='ie浏览器报告监听端口',type=str)
parser.add_argument('-chromeport', '--chromeport', help='chrome浏览器报告监听端口', type=str)
parser.add_argument('-firefoxport', '--firefoxport', help='friefox浏览器报告监听端口', type=str)
args=parser.parse_args()
ieport=args.ieport
chromeport=args.chromeport
firefoxport=args.firefoxport
if not ieport and not chromeport and not firefoxport:
sys.exit('请指定报告使用的端口,查看帮助:python generateReport.py --help')
else:
# 获得当前allure所有进程id
get_allure_process_ids_command = "ps -ef|grep -i allure\\.CommandLine|grep -v grep|awk '{print $2}'"
allure_process_ids = subprocess.check_output(get_allure_process_ids_command, shell=True)
allure_process_ids = allure_process_ids.decode('utf-8')
allure_process_ids = allure_process_ids.split('\n')
if ieport:
# 获得当前监听ie端口的进程id
get_ieport_process_ids_command = "netstat -anp|grep -i "+ieport+"|grep -v grep|awk '{print $7}'|awk -F '/' '{print $1}'"
ieport_process_ids = subprocess.check_output(get_ieport_process_ids_command,shell=True)
ieport_process_ids = ieport_process_ids.decode('utf-8')
ieport_process_ids = ieport_process_ids.split('\n')
is_find = False
for ieport_process_id in ieport_process_ids:
if is_find:
break
for allure_process_id in allure_process_ids:
allure_process_id = allure_process_id.strip()
ieport_process_id = ieport_process_id.strip()
if allure_process_id == ieport_process_id and not is_find and allure_process_id and ieport_process_id:
print('关闭allure进程,进程id:' + allure_process_id.strip() + ',该进程监听已监听端口:' + ieport)
subprocess.check_output("kill -9 " + allure_process_id.strip(), shell=True)
is_find =True
break
print('生成ie报告,使用端口'+ieport)
subprocess.check_output("nohup allure serve -p " + ieport + " output/ie >logs/ie_generateReport.log 2>&1 &",shell=True)
if chromeport:
# 获得当前监听chrome端口的进程id
get_chromeport_process_ids_command = "netstat -anp|grep -i " + chromeport + "|grep -v grep|awk '{print $7}'|awk -F '/' '{print $1}'"
chromeport_process_ids = subprocess.check_output(get_chromeport_process_ids_command, shell=True)
chromeport_process_ids = chromeport_process_ids.decode('utf-8')
chromeport_process_ids = chromeport_process_ids.split('\n')
is_find = False
for chromeport_process_id in chromeport_process_ids:
if is_find:
break
for allure_process_id in allure_process_ids:
allure_process_id = allure_process_id.strip()
chromeport_process_id = chromeport_process_id.strip()
if allure_process_id == chromeport_process_id and not is_find and allure_process_id and chromeport_process_id:
print('关闭allure进程,进程id:' + allure_process_id.strip() + ',该进程监听已监听端口:' + chromeport)
subprocess.check_output("kill -9 " + allure_process_id.strip(), shell=True)
is_find = True
break
print('生成chrome报告,使用端口' + chromeport)
subprocess.check_output("nohup allure serve -p " + chromeport + " output/chrome >logs/chrome_generateReport.log 2>&1 &",shell=True)
if firefoxport:
# 获得当前监听firefox端口的进程id
get_firefoxport_process_ids_command = "netstat -anp|grep -i " + firefoxport + "|grep -v grep|awk '{print $7}'|awk -F '/' '{print $1}'"
firefoxport_process_ids = subprocess.check_output(get_firefoxport_process_ids_command, shell=True)
firefoxport_process_ids = firefoxport_process_ids.decode('utf-8')
firefoxport_process_ids = firefoxport_process_ids.split('\n')
is_find = False
for firefoxport_process_id in firefoxport_process_ids:
if is_find:
break
for allure_process_id in allure_process_ids:
allure_process_id = allure_process_id.strip()
firefoxport_process_id = firefoxport_process_id.strip()
if allure_process_id == firefoxport_process_id and not is_find and allure_process_id and firefoxport_process_id:
print('关闭allure进程,进程id:'+allure_process_id.strip()+',该进程监听已监听端口:'+firefoxport)
subprocess.check_output("kill -9 " + allure_process_id, shell=True)
is_find = True
break
print('生成firefox报告,使用端口' + firefoxport)
subprocess.check_output("nohup allure serve -p " + firefoxport + " output/firefox >logs/firefox_generateReport.log 2>&1 &",shell=True)

View File

@ -0,0 +1,39 @@
import argparse
import subprocess
import sys
if __name__=='__main__':
parser=argparse.ArgumentParser()
parser.add_argument('-p','--port',help='报告监听的端口',type=str)
args=parser.parse_args()
port = args.port
if not port:
sys.exit('请指定报告使用的端口,查看帮助:python generateReport.py --help')
else:
# 获得当前allure所有进程id
get_allure_process_ids_command = "ps -ef|grep -i allure\\.CommandLine|grep -v grep|awk '{print $2}'"
allure_process_ids = subprocess.check_output(get_allure_process_ids_command, shell=True)
allure_process_ids = allure_process_ids.decode('utf-8')
allure_process_ids = allure_process_ids.split('\n')
# 获得当前监听port端口的进程id
get_port_process_ids_command = "netstat -anp|grep -i " + port + "|grep -v grep|awk '{print $7}'|awk -F '/' '{print $1}'"
port_process_ids = subprocess.check_output(get_port_process_ids_command, shell=True)
port_process_ids = port_process_ids.decode('utf-8')
port_process_ids = port_process_ids.split('\n')
is_find = False
for port_process_id in port_process_ids:
if is_find:
break
for allure_process_id in allure_process_ids:
allure_process_id = allure_process_id.strip()
port_process_id = port_process_id.strip()
if allure_process_id == port_process_id and not is_find and allure_process_id and port_process_id:
print('关闭allure进程,进程id:' + allure_process_id.strip() + ',该进程监听已监听端口:' + port)
subprocess.check_output("kill -9 " + allure_process_id.strip(), shell=True)
is_find = True
break
print('生成报告,使用端口' + port)
subprocess.check_output("nohup allure serve -p " + port + " output/api/ >logs/generateReport.log 2>&1 &",shell=True)

11
init/api/api_init.py Normal file
View File

@ -0,0 +1,11 @@
#-*- coding:utf8 -*-
from init.api.demoProject.demoProjectInit import DemoProjectInit
def api_init():
"""
初始化必要的数据
:return:
"""
# 初始化demoProject项目基础数据
DemoProjectInit().init()

View File

View File

@ -1,10 +1,13 @@
#-*- coding:utf8 -*-
from base.api.demoProject.api_demoProject_read_config import API_DemoProject_Read_Config
class DemoProjectInit:
def __init__(self):
pass
self._api_demoProject_read_config=API_DemoProject_Read_Config().config
def init(self):
if int(self._api_demoProject_read_config.init)==0:
return
#每次测试前先清除上次构造的数据
self._deinit()
#初始化必要的数据,如在数据库中构建多种类型的账号,

View File

View File

@ -0,0 +1,9 @@
#-*- coding:utf8 -*-
from init.app_ui.android.demoProject.demoProjectInit import DemoProjectInit
def android_init():
"""
初始化android项目必要的数据
:return:
"""
DemoProjectInit().init()

View File

@ -0,0 +1,18 @@
#-*- coding:utf8 -*-
from base.app_ui.android.demoProject.app_ui_android_demoProject_read_config import APP_UI_Android_DemoProject_Read_Config
class DemoProjectInit:
def __init__(self):
self._app_ui_android_demoProject_read_config=APP_UI_Android_DemoProject_Read_Config().config
def init(self):
if int(self._app_ui_android_demoProject_read_config.init)==0:
return
#每次测试前先清除上次构造的数据
self._deinit()
#初始化必要的数据,如在数据库中构建多种类型的账号,
pass
def _deinit(self):
#清除构造的数据
pass

View File

View File

@ -0,0 +1,8 @@
#-*- coding:utf8 -*-
def chrome_init():
"""
初始化ios项目必要的数据
:return:
"""
pass

View File

View File

@ -0,0 +1,8 @@
#-*- coding:utf8 -*-
def ios_init():
"""
初始化ios项目必要的数据
:return:
"""
pass

View File

View File

@ -0,0 +1,8 @@
#-*- coding:utf8 -*-
def windows_init():
"""
初始化Windows必要的数据
:return:
"""
pass

View File

@ -1,15 +0,0 @@
#-*- coding:utf8 -*-
from init.demoProject.demoProjectInit import DemoProjectInit
import configparser as ConfigParser
def init():
"""
初始化必要的数据
:return:
"""
config = ConfigParser.ConfigParser()
config.read('config/init.conf',encoding='utf-8')
#初始化demoProject项目基础数据
if 1==int(config.get('isInit','demoProject')):
DemoProjectInit().init()

View File

View File

@ -0,0 +1,19 @@
#-*- coding:utf8 -*-
from base.web_ui.demoProject.web_ui_demoProject_read_config import WEB_UI_DemoProject_Read_Config
class DemoProjectInit:
def __init__(self):
self._web_ui_demoProject_read_config=WEB_UI_DemoProject_Read_Config().config
def init(self):
if int(self._web_ui_demoProject_read_config.init)==0:
return
#每次测试前先清除上次构造的数据
self._deinit()
#初始化必要的数据,如在数据库中构建多种类型的账号,
pass
def _deinit(self):
#清除构造的数据
pass

View File

@ -0,0 +1,9 @@
#-*- coding:utf8 -*-
from init.web_ui.demoProject.demoProjectInit import DemoProjectInit
def init():
"""
初始化必要的数据
:return:
"""
DemoProjectInit().init()

0
packages/__init__.py Normal file
View File

View File

View File

0
packages/ios/__init__.py Normal file
View File

View File

View File

View File

View File

@ -1,7 +1,7 @@
#-*- coding:utf8 -*-
from page_objects.createElement import CreateElement
from page_objects.wait_type import Wait_Type as Wait_By
from page_objects.locator_type import Locator_Type
from page_objects.app_ui.wait_type import Wait_Type as Wait_By
from page_objects.app_ui.locator_type import Locator_Type
class StartPageElements:
def __init__(self):
self.start_btn = CreateElement.create(Locator_Type.ID, 'com.moji.mjweather:id/zc', wait_type=Wait_By.VISIBILITY_OF)

View File

@ -1,5 +1,5 @@
#-*- coding:utf-8 -*-
from page_objects.android.demoProject.elements.indexPageElements import IndexPageElements
from page_objects.app_ui.android.demoProject.elements.indexPageElements import IndexPageElements
class IndexPage:
def __init__(self,appOperator):

View File

@ -1,6 +1,6 @@
#-*- coding:utf-8 -*-
from page_objects.android.demoProject.elements.startPageElements import StartPageElements
from page_objects.android.demoProject.pages.indexPage import IndexPage
from page_objects.app_ui.android.demoProject.elements.startPageElements import StartPageElements
from page_objects.app_ui.android.demoProject.pages.indexPage import IndexPage
class StartPage:
def __init__(self,appOperator):

View File

@ -1,6 +1,6 @@
#-*- coding:utf-8 -*-
from page_objects.web_ui.demoProject import IndexPageElements
from page_objects.web_ui.demoProject import SecgroupPage
from page_objects.web_ui.demoProject.elements.indexPageElements import IndexPageElements
from page_objects.web_ui.demoProject.pages.network.secgroupPage import SecgroupPage
class IndexPage:
def __init__(self,browserOperator):
self._browserOperator=browserOperator

View File

@ -1,6 +1,6 @@
#-*- coding:utf-8 -*-
from page_objects.web_ui.demoProject import LoginPageElements
from page_objects.web_ui.demoProject import IndexPage
from page_objects.web_ui.demoProject.elements.loginPageElements import LoginPageElements
from page_objects.web_ui.demoProject.pages.indexPage import IndexPage
class LoginPage:
def __init__(self,browserOperator):
self._browserOperator=browserOperator

View File

@ -1,5 +1,5 @@
#-*- coding:utf-8 -*-
from page_objects.web_ui.demoProject import SecgroupPageElements
from page_objects.web_ui.demoProject.elements.network.secgroupPageElements import SecgroupPageElements
class SecgroupPage:
def __init__(self,browserOperator):
self._browserOperator=browserOperator

View File

@ -0,0 +1,11 @@
#-*- coding:utf8 -*-
class DemoProjectConfig:
def __init__(self):
self.url=None
self.adminUser=None
self.adminUserPassword = None
self.normalUser=None
self.normalUserPassword=None
self.closeUser=None
self.closeUserPassword=None
self.init=None

View File

View File

@ -0,0 +1,25 @@
# -*- coding:utf-8 -*-
class DemoProjectConfig:
def __init__(self):
self.platformName = None
self.automationName = None
self.platformVersion = None
self.deviceName = None
self.appActivity = None
self.appPackage = None
self.app = None
self.init = None
def get_desired_capabilities(self):
desired_capabilities={}
desired_capabilities.update({'platformName':self.platformName})
if self.automationName:
desired_capabilities.update({'automationName':self.automationName})
desired_capabilities.update({'platformVersion':self.platformVersion})
desired_capabilities.update({'deviceName':self.deviceName})
if self.appActivity and self.appPackage:
desired_capabilities.update({'appActivity':self.appActivity})
desired_capabilities.update({'appPackage':self.appPackage})
if self.app:
desired_capabilities.update({'app':self.app})
return desired_capabilities

View File

View File

View File

View File

View File

@ -1,67 +0,0 @@
#-*- coding:utf8 -*-
class DemoProjectConfig:
def __init__(self):
self._url=None
self._adminUser=None
self._adminUserPassword = None
self._normalUser=None
self._normalUserPassword=None
self._closeUser=None
self._closeUserPassword=None
@property
def url(self):
return self._url
@url.setter
def url(self,url):
self._url=url
@property
def adminUser(self):
return self._adminUser
@adminUser.setter
def adminUser(self,adminUser):
self._adminUser=adminUser
@property
def adminUserPassword(self):
return self._adminUserPassword
@adminUserPassword.setter
def adminUserPassword(self,adminUserPassword):
self._adminUserPassword=adminUserPassword
@property
def normalUser(self):
return self._normalUser
@normalUser.setter
def normalUser(self,normalUser):
self._normalUser=normalUser
@property
def normalUserPassword(self):
return self._normalUserPassword
@normalUserPassword.setter
def normalUserPassword(self,normalUserPassword):
self._normalUserPassword=normalUserPassword
@property
def closeUser(self):
return self._closeUser
@closeUser.setter
def closeUser(self,closeUser):
self._closeUser=closeUser
@property
def closeUserPassword(self):
return self._closeUserPassword
@closeUserPassword.setter
def closeUserPassword(self,closeUserPassword):
self._closeUserPassword=closeUserPassword

View File

View File

@ -0,0 +1,12 @@
# -*- coding:utf-8 -*-
class DemoProjectConfig:
def __init__(self):
self.browser_type = None
self.web_host = None
self.mysql_server = None
self.mysql_port = None
self.mysql_username = None
self.mysql_password = None
self.normal_username = None
self.normal_password = None
self.init = None

View File

@ -1,5 +1,5 @@
#-*- coding:utf8 -*-
from init.init import init
from init.api.api_init import api_init
import argparse
import pytest
import sys
@ -12,13 +12,13 @@ if __name__=='__main__':
# 初始化
print('开始初始化......')
init()
api_init()
print('初始化完成......')
# 执行pytest前的参数准备
pytest_execute_params=['-c', 'config/pytest.conf', '-v', '--alluredir', 'output/api/','--clean-alluredir']
# 判断目录参数
dir = 'cases'
dir = 'cases/api/'
if args.dir:
dir=args.dir
# 判断关键字参数

View File

@ -1,11 +1,11 @@
#-*- coding:utf8 -*-
from base.read_app_ui_config import Read_APP_UI_Config
from common.httpclient.doRequest import DoRequest
from common.java.javaTool import JavaTool
from init.android.android_init import android_init
from init.chrome.chrome_init import chrome_init
from init.ios.ios_init import ios_init
from init.winwos.windows_init import windows_init
from common.java.javaTools import JavaTool
from init.app_ui.android.android_init import android_init
from init.app_ui.chrome.chrome_init import chrome_init
from init.app_ui.ios.ios_init import ios_init
from init.app_ui.winwos.windows_init import windows_init
import argparse
import jpype
import json
@ -58,7 +58,7 @@ if __name__=='__main__':
# 执行pytest前的参数准备
pytest_execute_params=['-c', 'config/pytest.conf', '-v', '--alluredir', 'output/app_ui/','--clean-alluredir']
# 判断目录参数
dir = 'cases/'
dir = 'cases/app_ui/'
if args.dir:
dir=args.dir
# 判断关键字参数

View File

@ -1,8 +1,4 @@
#-*- coding:utf8 -*-
from init.init import init
import argparse
import pytest
import sys
if __name__=='__main__':
pass

View File

@ -2,7 +2,7 @@
from base.read_web_ui_config import Read_WEB_UI_Config
from common.fileTool import FileTool
from common.httpclient.doRequest import DoRequest
from init.init import init
from init.web_ui.web_ui_init import init
from selenium.webdriver.remote.remote_connection import RemoteConnection
from selenium.webdriver.remote.command import Command
import argparse
@ -45,7 +45,7 @@ if __name__=='__main__':
# 执行pytest前的参数准备
pytest_execute_params=['-c', 'config/pytest.conf', '-v', '--alluredir', 'output/web_ui/'+current_browser+'/','--clean-alluredir','-n',Read_WEB_UI_Config().web_ui_config.test_workers,'--dist','loadfile']
# 判断目录参数
dir = 'cases'
dir = 'cases/web_ui/'
if args.dir:
dir=args.dir
# 判断关键字参数