Appium+unittest+PageObject自动化测试框架综合实践

Appium自动化测试框架如下图:

框架中包含的脚本以此如下展示:

1.app目录下存放着测试需要的apk包

2.baseView目录下脚本中封装着所有页面需要的方法

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2019-07-04 17:17
# @Author : zhouyang
# @File : baseView.py


class BaseView(object):
    def __init__(self,driver):
        self.driver=driver
    def find_element(self,*loc):
        return self.driver.find_element(*loc)
    def find_elements(self,*loc):
        return self.driver.find_elements(*loc)
    def get_window_size(self):
        return self.driver.get_window_size()
    def swipe(self,start_x,start_y,end_x,end_y,duration):
        return self.driver.swipe(start_x,start_y,end_x,end_y,duration)

3.businessView目录下存放着所有页面具体实现的方法,比如登录页面,注册页面

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2019-07-05 11:42
# @Author : zhouyang
# @File : loginView.py

from common.desired_caps import appium_desired
from common.commom_fun import Commom
import logging
from selenium.webdriver.common.by import By
from selenium.common.exceptions import NoSuchElementException


class LoginView(Commom):
    username_type=(By.ID,'com.tal.kaoyan:id/login_email_edittext')
    password_type=(By.ID,'com.tal.kaoyan:id/login_password_edittext')
    submit_type=(By.ID,'com.tal.kaoyan:id/login_login_btn')

    tip_commit=(By.ID,'com.tal.kaoyan:id/tip_commit')

    button_mysefl=(By.ID,'com.tal.kaoyan:id/mainactivity_button_mysefl')
    usercenter_username=(By.ID,'com.tal.kaoyan:id/activity_usercenter_username')

    RightButton_textview=(By.ID,'com.tal.kaoyan:id/myapptitle_RightButton_textview')
    logout_text=(By.ID,'com.tal.kaoyan:id/setting_logout_text')


    def login_action(self,username,password):
        self.check_cancleBtn()
        self.check_skipBtn()
        logging.info('=================login===================')
        logging.info('input username:%s'%username)
        self.driver.find_element(*self.username_type).send_keys(username)

        logging.info('input password:%s' %password)
        self.driver.find_element(*self.password_type).send_keys(password)

        logging.info('click loginBtn')
        self.driver.find_element(*self.submit_type).click()
        logging.info('===============login finish==============')

    def check_account_alert(self):
        logging.info('========check_account_alert==========')
        try:
            element=self.driver.find_element(*self.tip_commit)
        except NoSuchElementException:
            pass
        else:
            logging.info('=======close alert=======')
            element.click()

    def check_loginStatus(self):
        logging.info('======check_loginStatus======')
        self.check_market_ad()
        self.check_account_alert()

        try:
            self.driver.find_element(*self.button_mysefl).click()
            element=self.driver.find_element(*self.usercenter_username)
        except NoSuchElementException:
            logging.error('========login false=========')
            self.get_screenshot('login fail')
            return False
        else:
            logging.info('=========login success==========')
            self.logout_action()
            return True

    def logout_action(self):
        logging.info('=========logout action=========')
        self.driver.find_element(*self.RightButton_textview).click()
        self.driver.find_element(*self.logout_text).click()
        self.driver.find_element(*self.tip_commit).click()


if __name__ == '__main__':
    driver=appium_desired()
    l=LoginView(driver)
    l.login_action('自学网2018','zxw208')
    l.check_loginStatus()

        
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2019-07-18 11:50
# @Author : zhouyang
# @File : register.py
'''注册模块'''

from common.commom_fun import Commom
from common.desired_caps import appium_desired
import logging
from selenium.webdriver.common.by import By
from selenium.common.exceptions import NoSuchElementException
from random import randint

class RegisterVeiw(Commom):
    register_text=(By.ID,'com.tal.kaoyan:id/login_register_text')

    register_userheader=(By.ID,'com.tal.kaoyan:id/activity_register_userheader')
    item_image=(By.ID,'com.tal.kaoyan:id/item_image')
    save=(By.ID,'com.tal.kaoyan:id/save')

    register_username=(By.ID,'com.tal.kaoyan:id/activity_register_username_edittext')
    register_password=(By.ID,'com.tal.kaoyan:id/activity_register_password_edittext')
    register_email=(By.ID,'com.tal.kaoyan:id/activity_register_email_edittext')
    register_btn=(By.ID,'com.tal.kaoyan:id/activity_register_register_btn')

    perfectinfomation_time=(By.ID,'com.tal.kaoyan:id/activity_perfectinfomation_time')
    text1=(By.ID,'android:id/text1')

    school_name=(By.ID,'com.tal.kaoyan:id/perfectinfomation_edit_school_name')
    forum_title=(By.ID,'com.tal.kaoyan:id/more_forum_title')
    university=(By.ID,'com.tal.kaoyan:id/university_search_item_name')

    perfectinfomation_major=(By.ID,'com.tal.kaoyan:id/activity_perfectinfomation_major')
    subject=(By.ID,'com.tal.kaoyan:id/major_subject_title')
    group=(By.ID,'com.tal.kaoyan:id/major_group_title')
    search_item=(By.ID,'com.tal.kaoyan:id/major_search_item_name')

    perfectinfomation_goBtn=(By.ID,'com.tal.kaoyan:id/activity_perfectinfomation_goBtn')

    button_mysefl = (By.ID, 'com.tal.kaoyan:id/mainactivity_button_mysefl')
    usercenter_username = (By.ID, 'com.tal.kaoyan:id/activity_usercenter_username')


    def register_action(self,username,password,email):
        logging.info('========register_action=========')
        self.check_cancleBtn()
        self.check_skipBtn()
        self.driver.find_element(*self.register_text).click() #点击注册

        #添加头像
        logging.info('set userheader')
        self.driver.find_element(*self.register_userheader).click()
        self.driver.find_elements(*self.item_image)[1].click()
        self.driver.find_element(*self.save).click()

        #填写用户名、密码、Email
        logging.info('username is %s'%username)
        self.driver.find_element(*self.register_username).send_keys(username)

        logging.info('password is %s' % password)
        self.driver.find_element(*self.register_password).send_keys(password)

        logging.info('email is %s' % email)
        self.driver.find_element(*self.register_email).send_keys(email)

        # 点击立即注册
        logging.info('register')
        self.driver.find_element(*self.register_btn).click()

        #判断是否进入注册信息页面
        try:
            self.driver.find_element(*self.perfectinfomation_time)
        except NoSuchElementException:
            logging.error('regiter fail')
            self.get_screenshot('regiter fail')
            return False
        else:
            self.add_register_info()
            if self.check_registerStatus():
                return True
            else:
                return False

    def add_register_info(self):
        logging.info('=======add_register_info=======')
        #填写年份
        self.driver.find_element(*self.perfectinfomation_time).click()
        self.driver.find_elements(*self.text1)[1].click() #2015

        #选择学校
        logging.info('select school')
        self.driver.find_element(*self.school_name).click()
        self.driver.find_elements(*self.forum_title)[1].click()
        self.driver.find_elements(*self.university)[1].click()

        #选择专业
        logging.info('select major')
        self.driver.find_element(*self.perfectinfomation_major).click()
        self.driver.find_elements(*self.subject)[1].click()
        self.driver.find_elements(*self.group)[2].click()
        self.driver.find_elements(*self.search_item)[0].click()

        #注册
        self.driver.find_element(*self.perfectinfomation_goBtn).click()

    def check_registerStatus(self):
        logging.info('========check_registerStatus==========')
        self.check_market_ad()

        try:
            self.driver.find_element(*self.button_mysefl).click()
            element = self.driver.find_element(*self.usercenter_username)
        except NoSuchElementException:
            logging.error('========register false=========')
            self.get_screenshot('register fail')
            return False
        else:
            logging.info('=========register success==========')
            return True

if __name__ == '__main__':
    driver=appium_desired()
    r=RegisterVeiw(driver)
    username='zxw2000'+'fly'+str(randint(1000,9999))
    password='zxw2000'+str(randint(1000,9999))
    email='zxw2000'+str(randint(1000,9999))+'@163.com'

    r.register_action(username,password,email)

4.common目录下存放着公共方法

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2019-07-04 17:22
# @Author : zhouyang
# @File : commom_fun.py

from common.desired_caps import appium_desired
from baseView.baseView import BaseView
from selenium.common.exceptions import NoSuchElementException
import logging,time,os,csv
from selenium.webdriver.common.by import By

class Commom(BaseView):
    cancelBtn=(By.ID,'android:id/button2')
    skipBtn=(By.ID,'com.tal.kaoyan:id/tv_skip')

    wemedia_cacel=(By.ID,'com.tal.kaoyan:id/view_wemedia_cacel')

    def check_cancleBtn(self):
        logging.info('===========check cancleBtn==========')
        try:
            cancelBtn = self.driver.find_element(*self.cancelBtn)
        except NoSuchElementException:
            logging.info('no cancelBtn')
        else:
            cancelBtn.click()

    def check_skipBtn(self):
        logging.info('===========check cancelBtn===========')
        try:
            skipBtn = self.driver.find_element(*self.skipBtn)
        except NoSuchElementException:
            logging.info('no cancelBtn')
        else:
            skipBtn.click()

    def get_size(self):
        x = driver.get_window_size()['width']
        y = driver.get_window_size()['height']
        return (x, y)

    def swipeLeft(self):
        l = self.get_size()
        x1 = int(l[0] * 0.9)
        x2 = int(l[0] * 0.1)
        y1 = int(l[1] * 0.5)
        driver.swipe(x1, y1, x2, y1, 1000)

    def getTime(self):
        self.now=time.strftime('%Y_%m_%d %H-%M-%S')
        return self.now

    def get_screenshot(self,module):
        time=self.getTime()
        image_file=os.path.dirname(os.path.dirname(__file__))+'/screenshot/%s_%s.png' %(module,time)

        logging.info('get %s screenshot'%module)
        self.driver.get_screenshot_as_file(image_file)

    def check_market_ad(self):
        logging.info('========check_market_ad=========')
        try:
            element=self.driver.find_element(*self.wemedia_cacel)
        except NoSuchElementException:
            pass
        else:
            logging.info('========close market========')
            element.click()

    def get_csv_data(self,csv_file,line):
        logging.info('======get_csv_data========')
        with open(csv_file,'r',encoding='utf-8-sig') as file:
            reader=csv.reader(file)
            for index,row in enumerate(reader,1):
                if index==line:
                    return row




if __name__ == '__main__':
    driver=appium_desired()
    com=Commom(driver)
    com.check_cancleBtn()
    com.check_skipBtn()

    # def get_csv_data(csv_file,line):
    #     with open(csv_file,'r',encoding='utf-8-sig') as file:
    #         reader=csv.reader(file)
    #         for index,row in enumerate(reader,1):
    #             if index==line:
    #                 return row
    #
    # csv_file='../data/account.csv'
    # data=get_csv_data(csv_file,1)
    # print(data)

    # lists=['这','是','一个','列表']
    # for index,row in enumerate(lists):
    #     print(index,row)
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2019-07-03 11:05
# @Author : zhouyang
# @File : capability_yaml.py
'''
从desired_caps.yaml文件中获取capability数据,登录考研帮app,把日志保存在文件中
'''
from appium import webdriver
import yaml
import logging
import logging.config
import os


CON_LOG='../config/log.conf'
logging.config.fileConfig(CON_LOG)
logging=logging.getLogger()

def appium_desired():
    with open('../config/desired_caps.yaml','r',encoding='utf-8') as file:
        data = yaml.load(file)

    desired_caps = {}
    desired_caps['platformName'] = data['platformName']
    desired_caps['platformVerion'] = data['platformVersion']
    desired_caps['deviceName'] = data['deviceName']
    #app使用相对路径
    base_dir = os.path.dirname(os.path.dirname(__file__))
    app_dir = os.path.join(base_dir, 'app', data['appname'])
    desired_caps['app'] = app_dir

    desired_caps['noReset'] = data['noReset']
    desired_caps['appPackage'] = data['appPackage']
    desired_caps['appActivity'] = data['appActivity']
    desired_caps['unicodeKeyboard'] = data['unicodeKeyboard']
    desired_caps['resetKeyboard'] = data['resetKeyboard']

    logging.info('start info...')

    driver = webdriver.Remote('http://' + str(data['ip']) + ':' + str(data['port']) + '/wd/hub', desired_caps)
    driver.implicitly_wait(8)
    return driver

if __name__ == '__main__':
    appium_desired()
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2019-07-05 16:18
# @Author : zhouyang
# @File : myunit.py

from common.desired_caps import appium_desired
import logging
import unittest
from time import sleep

class StartEnd(unittest.TestCase):
    def setUp(self):
        logging.info('===============setup==============')
        self.driver=appium_desired()

    def tearDown(self):
        logging.info('=============teardown============')
        sleep(5)
        self.driver.close_app()

5.config目录下存放所有配置文件,其中yaml文件中“:”后面一定要空一个格,否则会报错;.conf文件中内容,日志存放路径可以修改,日志表现形式等都可以修改

desired_caps.yaml

platformName: Android
platformVersion: 4.4.2
deviceName: 127.0.0.1:62001

#真机
#platformVersion: 4.4.2
#udid:
#deviceName: 127.0.0.1:62001
appname: kaoyan3.1.0.apk
appPackage: com.tal.kaoyan
appActivity: com.tal.kaoyan.ui.activity.SplashActivity
noReset: False
unicodeKeyboard: True
resetKeyboard: True
ip: 127.0.0.1
port: 4723

log.conf

[loggers]
keys=root,infoLogger

[logger_root]
level=DEBUG
handlers=consoleHandler,fileHandler

[logger_infoLogger]
handlers=consoleHandler,fileHandler
qualname=infoLogger
propagate=0

[handlers]
keys=consoleHandler,fileHandler

[handler_consoleHandler]
class=StreamHandler
level=INFO
formatter=form02
args=(sys.stdout,)

[handler_fileHandler]
class=FileHandler
level=INFO
formatter=form01
args=('../logs/runlog_conf.log', 'a')

[formatters]
keys=form01,form02

[formatter_form01]
format=%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s

[formatter_form02]
format=%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s

6.data目录中存放的是测试需要的数据,比如csv文件,Excel文件等

7.logs目录下存放测试过程中产生的日志,存放目录和日志名及日志内容均取决于log.conf文件中的 

args=('../logs/runlog_conf.log', 'a'),a表示追加,否则第二次执行测试后产生的日志会覆盖

8.reports目录存放测试过程中产生的测试报告,测试报告的路径,名称,内容等都在run_test.py文件中定义

9.screenshot目录存放测试过程中的截图,路径,名称等都取决于common目录下的common_fun.py文件下的get_screenshot()方法

10.test_case目录下存放着所有的测试用例

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2019-07-05 16:24
# @Author : zhouyang
# @File : test_login.py
'''
unittest写的测试用例
'''
import unittest
from common.myunit import StartEnd
from businessView.loginView import LoginView
import logging

class Test_Login(StartEnd):
    def test_login_zxw2018(self):
        logging.info('=========test_login_zxw2018=========')
        l=LoginView(self.driver)
        l.login_action('自学网2018','zxw2018')
        self.assertTrue(l.check_loginStatus())

    def test_login_zxw2017(self):
        logging.info('=========test_login_zxw2017=========')
        l=LoginView(self.driver)
        l.login_action('自学网2017','zxw2017')
        self.assertTrue(l.check_loginStatus())

    def test_login_error(self):
        logging.info('=========test_login_error=========')
        l=LoginView(self.driver)
        l.login_action('123','456')
        self.assertTrue(l.check_loginStatus(),msg='login fail')

if __name__ == '__main__':
    unittest.main()
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2019-07-22 16:32
# @Author : zhouyang
# @File : test_regiter.py
'''注册测试用例'''

from common.myunit import StartEnd
from businessView.register import RegisterVeiw
import logging
from random import randint
import unittest

class RegisterTest(StartEnd):

    def test_user_register(self):
        logging.info('=======start regiter========')
        r = RegisterVeiw(self.driver)
        username = 'zxw2000' + 'fly' + str(randint(1000, 9999))
        password = 'zxw2000' + str(randint(1000, 9999))
        email = 'zxw2000' + str(randint(1000, 9999)) + '@163.com'

        self.assertTrue(r.register_action(username, password, email))

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

11.test_run目录下存放着run_test.py文件,用于执行测试,生成测试报告,也可以在里面添加发送邮件功能,生成的测试报告直接以邮件形式发送到邮箱

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2019-07-22 16:42
# @Author : zhouyang
# @File : run_test.py
'''执行测试用例,生成测试报告'''

import unittest
import logging
# from BSTestRunner import BSTestRunner
from HTMLTestRunner import HTMLTestRunner
import time

#指定测试用例和测试报告的路径
report_dir='../reports/'
test_dir='../test_case'

#加载测试用例
discover=unittest.defaultTestLoader.discover(test_dir,pattern='test_login.py')

#定义报告的文件格式
now=time.strftime('%Y_%m_%d %H-%M-%S ')
report_name=report_dir+now+'test_report.html'

#运行用例并生成测试报告
with open(report_name,'wb') as f:
    runner=HTMLTestRunner(stream=f,title=u'login test repotr',description=u'kyb login test report')
    logging.info('start run testcasse......')
    runner(discover)


原文地址:https://www.cnblogs.com/xiuxiu123456/p/11326113.html