[python]appium+pytest+allure移动端自动化(二)框架篇

1 项目实例

框架如下:
在这里插入图片描述

2 必备信息统计

2.1 查看要测试的apk的信息

aapt dump badging xxx.apk

下图中框起来的即为appPackage
在这里插入图片描述
往下拉,找到launchable-activity,即为appActivity
在这里插入图片描述

2.2 查看模拟器地址、系统版本号

adb devices #查看模拟器地址

在这里插入图片描述
系统版本号:
在模拟器中“设置-关于”中查看
在这里插入图片描述

2.3 启动appium,并查看appium端口号

appium

在这里插入图片描述

2.4 查看安卓界面元素的路径

进入安卓adk的安装路径下的tools中,找到uiautomatorviewer.bat,如下:
在这里插入图片描述
需要先停掉appium,因为两者用的是相同的端口
1. 双击uiautomatorviewer.bat,启动
2. 在模拟器中打开被测app
3. 在uiautomatorviewer的界面中,点击工具栏第二个按钮 即可抓取模拟器当前的屏幕
在这里插入图片描述
我们要如何定位“立即进入”这个按钮呢,

  1. 点击上图中立即进入按钮
  2. 右侧展示的“resource-id”就是我们要用的
    enter_button = (By.ID, 'com.sdguodun.qyoa:id/skip')

2.5 查找内嵌h5中元素的路径

找到上文中安装的uc devtools,按照上文中的12345步 定位就可以。上文地址:[python]appium+pytest+allure移动端自动化(一)环境搭建篇

3 开始搭架子

IDE:我用的vscode,推荐使用pycharm

我们采用page object的架构

建个工程,叫appium
建几个二级文件夹 case、pages、util、log、result、report、log
顾名思义:
case 存放测试用例
pages 存放页面对象、方法
util 存放工具类,如日志
log 存放日志
result 存放测试结果,allure生成报告需要的测试结果
report 存放生成的allure测试报告
log 存放日志
注:建二级子目录后,pycharm会自动添加__init__.py,但是vscode不会,我们需要在case、pages、util目录下手动添加__init__.py

4 开始写

4.1 先写一个简单的demo

目的:这个demo的实现启动app、点击或者滑动一下

case目录下创建demo.py,键入如下内容:

# -*- encoding: utf-8 -*-
from appium import webdriver
import time

# apk参数
desired_caps = {
    'platformName': 'Android',
    'deviceName': '127.0.0.1:62001',  # 手机设备名称,通过adb devices查看
    'platformVersion': '5.1.1',  # android系统的版本号
    'appPackage': 'com.aiosign.dzonesign',  # apk包名
    # apk的launcherActivity
    'appActivity': 'com.aiosign.dzonesign.view.AppStartActivity',
    # 由于测试的app中内嵌了h5,所以需要用到chromedriver,
    # 下载对应版本的chromedriver.exe后放到D:\chromedriver路径中,在这里配置一下
    'chromedriverExecutableDir':'D:\chromedriver',
}
# 启动app
driver = webdriver.Remote('http://localhost:4723/wd/hub', desired_caps)
time.sleep(3)
# 我的app启动后是欢迎页,需要滑动
# 获取屏幕尺寸
sizes = driver.get_window_size()
# 滑动
driver.swipe(sizes.get('width')*0.9, sizes.get('height')*0.5, sizes.get('width')*0.2, sizes.get('height')*0.5)
# 加入已经跳转到h5中
# 打印所有的context
contexts = driver.contexts
print(contexts)
# ['NATIVE_APP', 'WEBVIEW_com.sdguodun.qyoa']
# NATIVE_APP 就是安卓原生的
# WEBVIEW_com.sdguodun.qyoa 就是h5的
# 切换到h5
driver.switch_to.context('WEBVIEW_com.sdguodun.qyoa')
# 定位到元素,输入test
driver.find_element_by_xpath('//*[@id="app"]/div[2]/div/div[1]/div[1]/div[1]/div[1]/div[2]/div/div/div/input').send_keys('test')


运行代码,看一下:
在这里插入图片描述

4.2 开始封装

4.2.1 封装PageObject

整个应用大概分为这么几个页面:

  • 欢迎页
  • 登录页
  • 主页

所以,我们在pages目录下创建一个BasePage.py用来存放一些公用的方法,比如查找元素、查找一组元素、输入、左右上下滑动、截图等

再创建三个py文件分别为WelcomePage.py LoginPage.py MainPage.py,继承自BasePage.py,如下:
在这里插入图片描述
BasePage.py内容如下:

# -*- encoding: utf-8 -*-
'''
@File    :   BasePage.py
@Time    :   2020/02/11 14:49:52
@Author  :   peace_su
@Version :   1.0
@Contact :   peace_su@163.com
@WebSite :   https://me.csdn.net/u010098760
'''

# here put the import lib
from selenium.webdriver.support.wait import WebDriverWait
# from appium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
import time
from util.LoggingUtil import LoggingUtil
import logging
import datetime


class BasePage(object):
    '''类注释
    详细描述

    Attributes:
        属性说明
    '''

    def __init__(self, driver):
        '''Class1类初始化方法

        Args:
            paramter1: 入参说明
            paramter2: 入参说明
        '''
        self.driver = driver
        LoggingUtil.setup_logging(LoggingUtil)
        self.logger = logging.getLogger()

    def find_element(self, *loc):
        '''重写查找单个元素方法

        '''
        try:
            WebDriverWait(self.driver, 5, 0.5).until(
                EC.visibility_of_element_located(loc))
            return self.driver.find_element(*loc)
        except Exception:
            # print('页面未找到元素')
            self.logger.error('未找到元素:%s 请检查!' % loc[1])
            screen_shot = self.get_screen('screen_shot/')
            self.logger.error('已截图,路径:%s' % screen_shot)

    def find_elements(self, *loc):
        '''重写查找多个元素方法

        '''
        try:
            WebDriverWait(self.driver, 5, 0.5).until(
                EC.visibility_of_element_located(loc))
            return self.driver.find_elements(*loc)
        except AttributeError:
            # print('页面未找到元素')
            self.logger.error('未找到元素:%s 请检查!' % loc[1])
            screen_shot = self.get_screen('screen_shot/')
            self.logger.error('已截图,路径:%s' % screen_shot)

    def send_keys(self, loc, vaule, clear_first=True, click_first=True):
        '''重写send_keys方法

        '''
        try:
            loc = getattr(self, "_%s" % loc)  # getattr相当于实现self.loc
            if click_first:
                self.find_element(*loc).click()
            if clear_first:
                self.find_element(*loc).clear()
                self.find_element(*loc).send_keys(vaule)
        except AttributeError:
            # print("页面中未能找到元素")
            self.logger.error('未找到元素:%s 请检查!' % loc[1])
            screen_shot = self.get_screen('screen_shot/')
            self.logger.error('已截图,路径:%s' % screen_shot)

    def is_toast_exist(self, text):
        '''is toast exist, return True or False
        :Agrs:
            - text   - 页面上看到的文本内容
        :Usage:
            is_toast_exist("看到的内容")
        '''
        try:
            toast_loc = (By.XPATH, ".//*[contains(@text,'%s')]" % text)
            WebDriverWait(self.driver, 5, 0.5).until(
                EC.presence_of_element_located(toast_loc))
            return True
        except Exception as e:
            print(e)
            self.logger.error('未找到元素:%s 请检查!' % text)
            screen_shot = self.get_screen('screen_shot/')
            self.logger.error('已截图,路径:%s' % screen_shot)
            return False

    def is_element_exist(self, *loc):
        try:
            WebDriverWait(self.driver, 5, 0.5).until(
                EC.visibility_of_element_located(loc))
            self.driver.find_element(*loc)
            return True
        except Exception:
            # print('页面未找到%s元素' % (loc))
            self.logger.error('未找到元素:%s 请检查!' % loc[1])
            screen_shot = self.get_screen('screen_shot/')
            self.logger.error('已截图,路径:%s' % screen_shot)
            return False

    def get_size(self):
        '''获取屏幕大小

        '''
        try:
            size = self.driver.get_window_size()
            return size
        except Exception:
            self.get_screen('screen_shot/')
            return None

    def swipe_to_left(self):
        '''左滑

        '''
        window_size = self.get_size()
        width = window_size.get("width")
        height = window_size.get("height")
        try:
            self.driver.swipe(width*0.9, height*0.5, width*0.1, height*0.5)
            time.sleep(0.5)
        except Exception:
            self.get_screen('screen_shot/')
        # self.driver.swipe(x*0.9, y*0.5, x*0.1, y*0.5)

    def swipe_to_right(self):
        '''右滑

        '''
        window_size = self.get_size()
        width = window_size.get("width")
        height = window_size.get("height")
        try:
            self.driver.swipe(width*0.1, height*0.5, width*0.9, height*0.5)
            time.sleep(0.5)
        except Exception:
            self.get_screen('screen_shot/')

    def swipe_to_up(self):
        '''上滑

        '''
        window_size = self.get_size()
        width = window_size.get("width")
        height = window_size.get("height")
        try:
            self.driver.swipe(width*0.5, height*0.9, width*0.5, height*0.1)
            time.sleep(0.5)
        except Exception:
            self.get_screen('screen_shot/')

    def swipe_to_down(self):
        '''下滑、下拉刷新

        '''
        window_size = self.get_size()
        width = window_size.get("width")
        height = window_size.get("height")
        try:
            self.driver.swipe(width*0.5, height*0.4, width*0.5, height*0.9)
            time.sleep(1)
        except Exception:
            self.get_screen('screen_shot/')

    def get_screen(self, path):
        '''截图

        '''
        now = datetime.datetime.now().strftime('%Y%m%d%H%M%S')
        screen_shot = path + 'a_' + now + '.png'
        self.driver.get_screenshot_as_file(screen_shot)
        return screen_shot


if __name__ == '__main__':
    print(123)

WelcomePage.py内容如下:

# -*- encoding: utf-8 -*-
'''
@File    :   WelcomePage.py
@Time    :   2020/02/11 14:49:29
@Author  :   peace_su
@Version :   1.0
@Contact :   peace_su@163.com
@WebSite :   https://me.csdn.net/u010098760
'''

# here put the import lib
from selenium.webdriver.common.by import By
from pages.BasePage import BasePage


class WelcomePage(BasePage):
    '''欢迎页面元素及方法
        该类继承自BasePage类

    Attributes:
        属性说明
    '''
    # 立即体验 按钮
    tiyan_button = (By.ID, 'com.aiosign.dzonesign:id/vStartNow')

    # 登录 按钮
    login_button = (By.ID, 'com.aiosign.dzonesign:id/btLoginUser')

    # 注册 按钮
    register_button = (By.ID, 'com.aiosign.dzonesign:id/btRegisterUser')

    # 点击立即体验按钮
    def click_tiyan_button(self):
        self.find_element(*self.tiyan_button).click()

    # 点击登录按钮
    def click_login_button(self):
        self.find_element(*self.login_button).click()

    # 点击注册按钮
    def click_register_button(self):
        self.find_element(*self.register_button).click()


if __name__ == '__main__':
    print(123)

LoginPage 和 MainPage内容略过

4.2.2 日志封装

暂时略过

4.3 写个测试用例测试下

单元测试使用pytest
unittest无法集成allure,htmltestrunner的日志太难看了

在case目录下建个test_login.py,如下:

# -*- encoding: utf-8 -*-
'''
@File    :   test_login.py
@Time    :   2020/03/05 14:28:40
@Author  :   peace_su
@Version :   1.0
@Contact :   peace_su@163.com
@WebSite :   https://me.csdn.net/u010098760
'''

# here put the import lib
import sys
import os
curPath = os.path.abspath(os.path.dirname(__file__))
rootPath = os.path.split(curPath)[0]
sys.path.append(rootPath)

import pytest
from util.LoggingUtil import LoggingUtil
from pages.BasePage import BasePage
from pages.WelcomePage import WelcomePage
from pages.LoginPage import LoginPage
from pages.MainPage import MainPage
import logging
from appium import webdriver
import time
import allure


@allure.feature('测试登录功能')
class Test_login(object):
    '''类注释
    详细描述

    Attributes:
        属性说明
    '''

    def setup_method(self, method):
        self.logging_util = LoggingUtil()
        self.logging_util.setup_logging()
        self.logger = logging.getLogger()
        desired_caps = {
            'platformName': 'Android',
            # 'deviceName': '127.0.0.1:5554',  # 手机设备名称,通过adb devices查看
            'deviceName': '127.0.0.1:62001',  # 手机设备名称,通过adb devices查看
            'platformVersion': '5.1.1',  # android系统的版本号
            'appPackage': 'com.aiosign.dzonesign',  # apk包名
            # apk的launcherActivity
            'appActivity': 'com.aiosign.dzonesign.view.AppStartActivity',
        }
        self.logger.info('启动app')
        try:
            self.driver = webdriver.Remote('http://localhost:4723/wd/hub', desired_caps)
            time.sleep(3)
            self.base_page = BasePage(self.driver)
            self.welcome_page = WelcomePage(self.base_page)
            self.login_page = LoginPage(self.base_page)
            self.main_page = MainPage(self.base_page)
        except Exception:
            BasePage.get_screen('screen_shot/')

    def teardown_method(self, method):
        print("断开连接")

    @allure.story('登录-用户不存在')
    def test_login_1(self):
        self.logger.info('开始测试test_login_1==================================')
        self.base_page.swipe_to_left()
        self.base_page.swipe_to_left()
        self.welcome_page.click_tiyan_button()
        self.welcome_page.click_login_button()
        self.login_page.input_username('15628811988')
        self.login_page.input_passwd('12345678')
        self.login_page.click_signin_button()
        res = self.base_page.is_toast_exist('用户不存在')
        assert res
        self.logger.info('测试结束=====================================')

    @allure.story('登录-账号或密码错误')
    def test_login_2(self):
        self.base_page.swipe_to_left()
        self.base_page.swipe_to_left()
        self.welcome_page.click_tiyan_button()
        self.welcome_page.click_login_button()
        self.login_page.input_username('123456')
        self.login_page.input_passwd('abc23')
        self.login_page.click_signin_button()
        res = self.base_page.is_toast_exist('账号或密码错误')
        assert res

    @allure.story('登录-正常')
    def test_right(self):
        self.logger.info('开始测试test_right=====================================')
        self.base_page.swipe_to_left()
        self.base_page.swipe_to_left()
        self.welcome_page.click_tiyan_button()
        self.welcome_page.click_login_button()
        self.login_page.input_username('123456')
        self.login_page.input_passwd('abcabc')
        self.login_page.click_signin_button()
        res = self.main_page.is_personal_center_button_exist()
        assert res
        # self.main_page.click_personal_center_button()
        # self.logger.info('测试完成')

    @allure.story('登录-未输入密码')
    def test_login_3(self):
        self.base_page.swipe_to_left()
        self.base_page.swipe_to_left()
        self.welcome_page.click_tiyan_button()
        self.welcome_page.click_login_button()
        self.login_page.input_username('123456')
        # self.login_page.input_passwd('')
        self.login_page.click_signin_button()
        res = self.base_page.is_toast_exist('请输入密码!')
        assert res

    @allure.story('登录-账号格式不正确')
    def test_login_4(self):
        self.base_page.swipe_to_left()
        self.base_page.swipe_to_left()
        self.welcome_page.click_tiyan_button()
        self.welcome_page.click_login_button()
        self.login_page.input_username('156288119')
        self.login_page.input_passwd('abcabc')
        self.login_page.click_signin_button()
        res = self.base_page.is_toast_exist('账号格式不正确(邮箱、信用代码、手机)')
        assert res

    @allure.story('登录-未输入账号')
    def test_login_5(self):
        self.base_page.swipe_to_left()
        self.base_page.swipe_to_left()
        self.welcome_page.click_tiyan_button()
        self.welcome_page.click_login_button()
        # self.login_page.input_username('')
        self.login_page.input_passwd('abcabc')
        self.login_page.click_signin_button()
        res = self.base_page.is_toast_exist('请输入账号!')
        assert res


if __name__ == '__main__':
    pytest.main(["--reruns", "3", "--reruns-delay", "2", "--alluredir", "result"])

运行此文件,控制台输出执行情况,如下:
别忘了启动appium和模拟器哦,在模拟器里预先安装好待测试的apk
在这里插入图片描述
其中.是测试通过的,R是重试的,F是失败的,E是出现异常的

4.4 生成个报告

在cmd中执行如下命令,生成测试报告:

allure generate result -o report --clean

在这里插入图片描述
接下来,我们需要去vscode中找到report目录下的index.html,用live server打开
在这里插入图片描述
pycharm中右键,用浏览器打开即可
直接在文件夹中双击index.html在浏览器中打开不行哦
报告如下:
在这里插入图片描述
可以点击各个具体的菜单,查看具体信息。

至此,一个大的架子基本搭建完成了。


快来联系我吧
在这里插入图片描述

  • 5
    点赞
  • 53
    收藏
    觉得还不错? 一键收藏
  • 9
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值