• webUI自动化之基本框架搭建(python + selenium + unittest)


    一、 前言

    该文档描述一次基于python的webUI自动化框架搭建过程及简单的使用。

    框架构成:python + selenium + unittest

    二、准备及编写条件

    准备:

    • IDEA工具:pycharm(社区版即可)
    • Python3.9
    • Webdriver.exe文件下载好,将该文件放在本地python的lib文件夹下,并将其配置到环境变量(实际上放到python的任意文件夹下都可以,但必须将其配置到环境变量,以便于运行时能找到这个文件)。或者在代码中指定驱动的路径也可以。如:driver
      = webdriver.Chrome(executable_path=‘driver/chromedriver.exe’)
    • Chrome浏览器

    注意:Webdriver.exe的版本需要和浏览器版本一致,不一致则会报错,浏览器版本可通过浏览器 “设置” --> “关于Chrome” 查看

    Webdriver.exe下载地址:http://chromedriver.storage.googleapis.com/index.html

    编写条件:

    需掌握python基础、元素定位、三种等待方式、鼠标键盘事件、窗口切换等(多多益善)

    三、初体验

    1、实现用户登录:

     1 from time import sleep
     2 from selenium import webdriver
     3 from selenium.webdriver.common.by import By
     4 
     5 driver = webdriver.Chrome()
     6 driver.get(r'https://xxx')      # 打开浏览器并访问该链接,这里的链接不便展示哈
     7 driver.maximize_window()
     8 
     9 # 定位元素并操作
    10 driver.find_element(By.NAME, 'username').send_keys('luoyang')
    11 driver.find_element(By.NAME, 'password').send_keys('123456')
    12 driver.find_element(By.XPATH, '//*[@id="app"]/div/div[2]/div/form/button').click()
    13 sleep(10)
    14 
    15 # 关闭并退出浏览器
    16 driver.quit()
    17 
    18  
    19 
    20 # 关于close()和quit():close()只是关闭浏览器当前窗口,并不会退出浏览器
    21 
    22 # 当浏览器只有一个窗口时,使用close()虽然退出了浏览器,但驱动还在运行
    23 
    24 # 而quit()则会关闭所有窗口,清除session,并结束驱动运行
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    2、进一步实现,引入unittest框架

     1 from time import sleep
     2 from selenium import webdriver
     3 import unittest
     4 from selenium.webdriver.common.by import By
     5 
     6 
     7 class Login(unittest.TestCase):
     8 
     9     def setUp(self) -> None:
    10         self.driver = webdriver.Chrome()
    11         self.url = r'https://xxx'
    12         self.driver.maximize_window()  # 最大化窗口
    13         self.driver.get(self.url)
    14 
    15     def test_login(self, username='luoyang', password='123456'):
    16         self.driver.find_element(By.NAME, 'username').send_keys(username)
    17         self.driver.find_element(By.NAME, 'password').send_keys(password)
    18         self.driver.find_element(By.XPATH, '//*[@id="app"]/div/div[2]/div/form/button').click()
    19 
    20     def tearDown(self) -> None:
    21         sleep(5)
    22         self.driver.quit()
    23 
    24 
    25 if __name__ == '__main__':
    26     unittest.main()     # 执行测试
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    关于unittest框架详细学了之后再在其他篇章中发表…

    四、POM设计模式

    1、POM:

    即page object model,页面对象模型,顾名思义,就是将每个页面当做一个对象来看待,将页面中需要操作的元素提取到这个对象中,此后每当要用到这些元素时,调用该对象即可。让我们来具体使用一下吧!

    首先,我们先创建好结构:

    all_case_run.py --模块,用于执行所有的测试类

    |–common – 包,用于存放公用的工具类

    |–universal_method.py – 通用工具类

    |–case – 包,用于存放所有的测试类

    |–test_login.py – 登录测试用例类

    |–pages – 包,用于存放页面类及页面基类(basePage)

    |–base_page.py – 所有页面对象都需继承该类,该类里封装了元素的定位、操作等方法

    |–login_page.py – 登录页面类,该类包含了登录页面的元素、元素定位及操作逻辑等

    |–data – 包,用于存放元素定位路径文件

    |–login.yaml – yaml数据文件,用于存放登录页面的元素定位路径数据

    |–report – 包,用于存放测试报告文件及日志文件(自动生成)

    至此,一个简便的结构就创建好了。

    2、 all_case.run.py内容:

     1 import time
     2 
     3 from BeautifulReport import BeautifulReport
     4 
     5 from GAD_webUI.commen.universal_method import UniversalMethod
     6 
     7 from GAD_webUI.commen.send_email import SendEmail
     8 
     9 
    10 
    11 if __name__ == '__main__':
    12 
    13 
    14     print('用例开始执行-------------------')
    15 
    16     now = time.strftime('%Y-%m-%d_%H_%M_%S', time.localtime(time.time()))
    17 
    18     filename = 'D:\\testStudy\gitstudy\gitrepository\pythonstudy\pythonworkspace\GAD_webUI\\report'
    19 
    20     UniversalMethod.del_report(filename, 3)    # 测试报告生成之前删除冗余的测试报告(最多只剩3个测试报告),这里的 del_report() 方法是自定义的
    21 
    22 
    23 
    24     result = BeautifulReport(UniversalMethod.createSuite())     # 创建测试套件容器,这里的 createSuite() 方法也是自定义的
    25 
    26     result.report(filename=now+'GAD_smoke', description='GAD冒烟测试', report_dir=filename)     # 生成测试报告,这里采用的是 BeautifulReport
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    3、 login.yaml内容:

    1 username: //*[@id="app"]/div/div[2]/div/form/div[2]/div/div/input
    2 
    3 password: //*[@id="app"]/div/div[2]/div/form/div[3]/div/div/input
    4 
    5 login_btn: //*[@id="app"]/div/div[2]/div/form/button  # 登录按钮 xpath路径
    6 
    7 login_error: /html/body/div[3]/div  # 用户登录失败出现的元素
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    这里还需要优化一下,因为我全部采用的是xpath定位

    Python获取yaml文件的内容:

     1 # 读取yaml文件并返回一个数据集,返回的是一个字典
     2 
     3 @staticmethod
     4 
     5 def get_yaml_info(
     6 
     7         yaml_path=r'D:\G_webUI\data\advertisingId_page.yaml'):
     8 
     9     yaml_file = open(yaml_path, 'r', encoding='utf-8')
    10 
    11     content = yaml.load(yaml_file, Loader=yaml.FullLoader)
    12 
    13     return content
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    能读取yaml文件的内容,能干什么不用多说了吧

    4、 basePage.py内容:

      1 """
      2 
      3 所有页面类都需继承该类,该类封装了Selenium 基本方法(元素定位、元素等待、鼠标事件等)
      4 
      5 """
      6 
      7 import random
      8 
      9 
     10 
     11 from selenium.common.exceptions import NoSuchElementException, TimeoutException
     12 
     13 from selenium.webdriver import Keys
     14 
     15 from selenium.webdriver.common.by import By
     16 
     17 from selenium.webdriver.support.wait import WebDriverWait
     18 
     19 
     20 
     21 from selenium.webdriver.support import expected_conditions as EC
     22 
     23 
     24 
     25 from GAD_webUI.commen.universal_method import UniversalMethod
     26 
     27 from selenium.webdriver.common.action_chains import ActionChains
     28 
     29 
     30 
     31 
     32 
     33 class BasePage(object):
     34 
     35     def __init__(self, driver, path='https://xxx'):
     36 
     37         self.driver = driver
     38 
     39         self.url = path
     40 
     41         self.driver.implicitly_wait(30)  # 隐式等待,设置一次全局有效
     42 
     43         self.driver.maximize_window()
     44 
     45         self.navigation_els = UniversalMethod.get_yaml_info(
     46 
     47 r'D:\G_webUI\data\navigation.yaml')
     48 
     49 
     50 
     51     def open_page(self):
     52 
     53         self.driver.get(self.url)   # 打开浏览器
     54 
     55 
     56 
     57     # 单个元素的定位方法1
     58 
     59     def find_element(self, *args):
     60 
     61         try:
     62 
     63             return self.driver.find_element(*args)
     64 
     65         except NoSuchElementException:
     66 
     67             print("未找到该元素:" + str(args))
     68 
     69 
     70 
     71     # 单个元素的定位方法2
     72 
     73     def find_element_v(self, *args):
     74 
     75         try:
     76 
     77             return WebDriverWait(self.driver, 5, 0.5).until(EC.visibility_of_element_located(*args))
     78 
     79         except (NoSuchElementException, TimeoutException):
     80 
     81             print("超过元素定位等待时长,无法获取到该元素,请检查定位路径")
     82 
     83 
     84 
     85     # 单个元素的定位方法3
     86 
     87     def find_element_p(self, *args):
     88 
     89         try:
     90 
     91             return WebDriverWait(self.driver, 5, 0.5).until(EC.presence_of_element_located(*args))
     92 
     93         except (NoSuchElementException, TimeoutException):
     94 
     95             print("超过元素定位等待时长,无法获取到该元素,请检查定位路径")
     96 
     97 
     98 
     99     # 多个元素的定位方法1
    100 
    101     def find_elements(self, *loc):
    102 
    103         try:
    104 
    105             return self.driver.find_elements(*loc)
    106 
    107         except (NoSuchElementException, TimeoutException):
    108 
    109             print("未找到该元素:" + str(loc))
    110 
    111 
    112 
    113     # 多个元素的定位方法2
    114 
    115     def find_elements_v(self, *loc):
    116 
    117         try:
    118 
    119             return WebDriverWait(self.driver, 5, 0.5).until(EC.visibility_of_any_elements_located(*loc))
    120 
    121         except (NoSuchElementException, TimeoutException):
    122 
    123             print("超过元素定位等待时长,无法获取到该元素,请检查定位路径")
    124 
    125 
    126 
    127     # 多个元素的定位方法3
    128 
    129     def find_elements_p(self, *loc):
    130 
    131         try:
    132 
    133             return WebDriverWait(self.driver, 5, 0.5).until(EC.presence_of_all_elements_located(*loc))
    134 
    135         except (NoSuchElementException, TimeoutException):
    136 
    137             print("超过元素定位等待时长,无法获取到该元素,请检查定位路径")
    138 
    139 
    140 
    141     # 点击元素,以JS脚本的方式
    142 
    143     def click_JS(self, element):
    144 
    145         self.driver.execute_script('arguments[0].click();', element)
    146 
    147 
    148 
    149     # 点击元素,普通方式
    150 
    151     def click(self, element_xp):
    152 
    153         try:
    154 
    155             self.find_element_p((By.XPATH, element_xp)).click()
    156 
    157         except AttributeError:
    158 
    159             print('元素获得为空,无属性可用')
    160 
    161 
    162 
    163     # 清除输入框
    164 
    165     def clear_input(self, element_xp):
    166 
    167         try:
    168 
    169             self.find_element_p((By.XPATH, element_xp)).clear()
    170 
    171         except AttributeError:
    172 
    173             print('元素获得为空,无属性可用')
    174 
    175 
    176 
    177     # 输入框输入值
    178 
    179     def send_kw(self, element_xp, kw):
    180 
    181         try:
    182 
    183             self.find_element_p((By.XPATH, element_xp)).send_keys(kw)
    184 
    185         except AttributeError:
    186 
    187             print('元素获得为空,无属性可用')
    188 
    189 
    190 
    191     # 模拟键盘向页面发送end指令(滑动到页面底部)
    192 
    193     def page_end(self, table_xp):
    194 
    195         try:
    196 
    197             self.find_element_p((By.XPATH, table_xp)).send_keys(Keys.END)
    198 
    199         except AttributeError:
    200 
    201             print('元素获得为空,无属性可用')
    202 
    203 
    204 
    205     # 鼠标移动到指定元素上
    206 
    207     def move_element(self, element_xp):
    208 
    209         move = self.find_element_p((By.XPATH, element_xp))
    210 
    211         ActionChains(self.driver).move_to_element(move).perform()
    212 
    213 
    214 
    215     # 双击元素
    216 
    217     def double_click(self, element):
    218 
    219         ActionChains(self.driver).double_click(element).perform()
    220 
    221 
    222 
    223     # 切换到指定窗口
    224 
    225     def switch_window(self, num):
    226 
    227         handles = self.driver.window_handles  # 获取当前窗口句柄集合
    228 
    229         try:
    230 
    231             self.driver.switch_to.window(handles[num])  # 切换到指定窗口
    232 
    233         except Exception:
    234 
    235             raise
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227
    • 228
    • 229
    • 230
    • 231
    • 232
    • 233
    • 234
    • 235

    5、 loginPage.py内容:

     1 from GAD_webUI.commen.universal_method import UniversalMethod
     5 from GAD_webUI.pages.base_page import BasePage
    10 
    
    11 class LoginPage(BasePage):
    12 
    13     login_els = UniversalMethod.get_yaml_info(
    14 
    15         r'D:\G_webUI\data\login.yaml')  # login_els是个字典
    16 
    17 
    18 
    19     def login_GAD(self, username, password):
    20 
    21         self.open_page()  # 打开浏览器
    22 
    23         self.send_kw(self.login_els['username'], username)  # 输入用户名
    24 
    25         self.send_kw(self.login_els['password'], password)  # 输入密码
    26 
    27         self.click(self.login_els['login_btn'])  # 点击登录
    28 
    29         sleep(2)
    30 
    31 
    32 
    33         error_el = self.find_element_p((By.XPATH, self.login_els['login_error']))
    34 
    35         if error_el:
    36 
    37             return error_el.text
    38 
    39         else:
    40 
    41             print('登录成功')
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    6、 test_login.py(测试类)内容:

     1 import unittest
     2 
     3 from time import sleep
     4 
     5 from selenium import webdriver
     6 
     7 from GAD_webUI.pages.login_page import LoginPage
     8 
     9 
    10 
    11 class Login(unittest.TestCase):
    12 
    13     driver = webdriver.Chrome()
    14 
    15 
    16 
    17     @classmethod
    18 
    19     def setUpClass(cls, ) -> None:
    20 
    21         cls.login_page = LoginPage(cls.driver)
    22 
    23 
    24 
    25     def test_login(self, username='v-luoyang', password='123456'):
    26 
    27         error_text = self.login_page.login_GAD(username, password)
    28 
    29         self.assertFalse(error_text is not None, msg=error_text)  # 如果错误信息存在,则登录失败,输出错误提示信息
    30 
    31 
    32 
    33     @classmethod
    34 
    35     def tearDownClass(cls) -> None:
    36 
    37         sleep(5)
    38 
    39         cls.driver.quit()
    40 
    41 
    42 
    43 if __name__ == '__main__':      # 执行all_test_run.py 时,需将该段注释掉
    44 
    45     unittest.main() 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45

    7、 引入测试报告

    在小节2中已经实现了。

    result = BeautifulReport(UniversalMethod.createSuite()) # 创建测试套件容器
        result.report(filename=now+‘G_smoke’, description=‘G冒烟测试’, report_dir=filename) # 生成测试报告

    8、 执行完测试自动发送邮件(经尝试可用)

      1 import os
      2 
      3 
      4 
      5 """
      6 
      7 这个文件主要是配置发送邮件的主题、正文等,将测试报告发送并抄送到相关人邮箱的逻辑。
      8 
      9 """
     10 
     11 import smtplib
     12 
     13 from email.mime.text import MIMEText
     14 
     15 from email.mime.multipart import MIMEMultipart
     16 
     17 
     18 
     19 class SendEmail(object):
     20 
     21     def __init__(self, username, passwd, recv, title, content,
     22 
     23                  file_path=None, ssl=False,
     24 
     25                  email_host='smtp.163.com', port=25, ssl_port=465):
     26 
     27         self.username = username  # 用户名
     28 
     29         self.passwd = passwd  # 密码
     30 
     31         self.recv = recv  # 收件人,多个要传list ['a@qq.com','b@qq.com]
     32 
     33         self.title = title  # 邮件标题
     34 
     35         self.content = content  # 邮件正文
     36 
     37         self.file_path = file_path  # 附件路径,如果不在当前目录下,要写绝对路径
     38 
     39         self.email_host = email_host  # smtp服务器地址
     40 
     41         self.port = port  # 普通端口
     42 
     43         self.ssl = ssl  # 是否安全链接
     44 
     45         self.ssl_port = ssl_port  # 安全链接端口
     46 
     47 
     48 
     49     # 发送邮件
     50 
     51     def send_email(self):
     52 
     53         msg = MIMEMultipart()
     54 
     55         msg.attach(MIMEText(self.content))  # 邮件正文的内容
     56 
     57 
     58 
     59         # 构造附件
     60 
     61         for f_path, file_dirs, files in os.walk(self.file_path):
     62 
     63             for file in files:
     64 
     65                 msg.attach(self._att_html(os.path.join(f_path, file)))
     66 
     67 
     68 
     69         msg['Subject'] = self.title  # 邮件主题
     70 
     71         msg['From'] = self.username  # 发送者账号
     72 
     73         msg['To'] = ','.join(self.recv)  # 接收者账号列表
     74 
     75         if self.ssl:
     76 
     77             self.smtp = smtplib.SMTP_SSL(self.email_host, port=self.ssl_port)
     78 
     79         else:
     80 
     81             self.smtp = smtplib.SMTP(self.email_host, port=self.port)
     82 
     83         # 发送邮件服务器的对象
     84 
     85         self.smtp.login(self.username, self.passwd)
     86 
     87         try:
     88 
     89             self.smtp.sendmail(self.username, self.recv, msg.as_string())
     90 
     91             pass
     92 
     93         except Exception as e:
     94 
     95             print('出错了。。', e)
     96 
     97         else:
     98 
     99             print('发送成功!')
    100 
    101         self.smtp.quit()
    102 
    103 
    104 
    105     # 构造邮件附件
    106 
    107     @staticmethod
    108 
    109     def _att_html(filename):
    110 
    111         # 构造附件
    112 
    113         atthtml = MIMEText(open(filename, 'rb').read(), 'base64',
    114 
    115                            'utf-8')  # 文件放在同一路径,不放在同一路径改一下比如'D:/test/report.html
    116 
    117         atthtml["Content-Type"] = 'application/octet-stream'
    118 
    119         atthtml["Content-Disposition"] = 'attachment;filename = "GAD_Smoke_report.html"'
    120 
    121         return atthtml
    122 
    123 
    124 
    125 
    126 # 调用并发送邮件
    127 if __name__ == '__main__':
    128 
    129     m = SendEmail(
    130 
    131         username='cicada_luo@163.com',  # 这里填发送者邮箱
    132 
    133         passwd='TCBXOAOF...',    # 授权码还是什么忘记了
    134 
    135         recv=['1761885773@qq.com'],  # 接收者邮箱
    136 
    137         title='G——smoke',
    138 
    139         content='G——smoke测试报告',
    140 
    141         file_path='D:\\G_webUI\\report',  # 发送的文件
    142 
    143         email_host='smtp.163.com',
    144 
    145         ssl_port=465,
    146 
    147         ssl=True,
    148 
    149     )
    150 
    151     m.send_email()  # 发送邮件
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151

    9、 引入DDT

    ① 安装DDT,打开cmd,输入pip install ddt

    ② 在测试类上写上@ddt,表示该用例类需要进行数据驱动

    ③ 在测试方法上写上@file_data(file_path),表示引入外部文件进行数据驱动。

    ④ 如果步骤③传入的文件是yaml格式,那么用例方法参数需要用**args来接收文件的内容(表示接收文件的所有内容到该参数中) ;如果传入的文件是其他的格式,那么用一个参数接收即可(接收的是json数据格式的值)

     1 import unittest
     2 
     3 
     4 
     5 from ddt import file_data, ddt
     6 
     7 from selenium import webdriver
     8 
     9 from GAD_webUI.pages.login_page import LoginPage
    10 
    11 
    12 
    13 
    14 
    15 @ddt
    16 
    17 class test_Login(unittest.TestCase):
    18 
    19 
    20 
    21     def setUp(self) -> None:
    22 
    23         self.driver = webdriver.Chrome()
    24 
    25         self.login_page = LoginPage(self.driver)
    26 
    27 
    28 
    29     def tearDown(self) -> None:
    30 
    31         self.driver.quit()
    32 
    33 
    34 
    35     @file_data(r'D:\G_webUI\data\user_login.yaml')
    36 
    37     def test_login(self, **kwargs):
    38 
    39         print(kwargs)
    40 
    41         error_text = self.login_page.login_GAD(kwargs['username'], kwargs['password'])
    42 
    43         self.assertFalse(error_text is not None, msg=error_text)  # 如果错误信息存在,则登录失败,输出错误提示信息
    44 
    45         print("------------------------------------")
    46 
    47 
    48 
    49 
    50 
    51 if __name__ == '__main__':  # 执行all_test_run.py 时,需将该段注释掉
    52 
    53     unittest.main()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53

    五、所遇问题及解决思路

    1、元素定位不到怎么办

    表现形式为:程序抛出 NoSuchElementException 异常

    解决思路:

    ① 检查元素定位属性值是否写错,很多时候错误都是因为粗心导致的。

    ② 添加等待。

    有时,程序执行过快,导致程序已经执行完了,而元素还未加载出来,那么就会抛出异常,

    我们添加等待时间即可。最粗暴的做法就是 sleep(3)—强制等待3秒,这样做使得程序运行时间较长,一般少用。最常用的是使用显示等待(搭配 until()方法、expected_conditions 类来使用)。例:

    1 from selenium.common.exceptions import NoSuchElementException, TimeoutException
     2 from selenium.webdriver.support import expected_conditions as EC
     3  
     4 
     5 # 单个元素的定位方法3
     6 
     7 def find_element_p(self, *args):
     8 
     9     try:
    10 
    11         return WebDriverWait(self.driver, 5, 0.5).until(EC.presence_of_element_located(*args))
    12 
    13     except (NoSuchElementException, TimeoutException):
    14 
    15         print("超过元素定位等待时长,无法获取到该元素,请检查定位路径")
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    ③ 以上方法不行时,那么就再尝试使用其他方式进行元素定位(常见的元素定位方式可是有八种之多)

    ④ 当使用xpath定位时,有可能,定位元素之前进行了某些操作,但程序逻辑没有进行这些操作,那么就可能导致定位元素的xpath路径不一致,从而导致定位元素失败。如某些元素需要点击才能出现,但你的脚本程序未进行点击操作,自然就不可能定位得到该元素了。

    2、元素无法交互

    表现形式为:程序抛出异常:ElementNotInteractableException: Message: element not interactable

    解决思路:

    ① 检查进行交互的元素是否唯一,元素不唯一时也会出现此类错误

    ② 检查元素是否被隐藏。如果元素被隐藏起来,也无法进行交互。常见案例有:某按钮需要鼠标悬停在该元素上才能进行交互操作,此时就需要用到 ActionChains 类。例:

    1 # 鼠标移动到指定元素上
    2 
    3 def move_element(self, element_xp):
    4 
    5     move = self.find_element_p((By.XPATH, element_xp))
    6 
    7     ActionChains(self.driver).move_to_element(move).perform()      
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    ③检查元素是否被其他元素遮挡。当元素被其他元素所遮盖,那么也无法对该元素进行操作。解决办法就是移除其他元素的遮盖(通过脚本操作遮盖元素移开被遮盖的元素)。

    以上脚本虽然较为粗糙,但也五脏俱全,后面熟悉了再回来补充,欢迎各位前来指正。

    最后: 可以在公众号:伤心的辣条 ! 自行领取一份216页软件测试工程师面试宝典文档资料【免费的】。以及相对应的视频学习教程免费分享!,其中包括了有基础知识、Linux必备、Shell、互联网程序原理、Mysql数据库、抓包工具专题、接口测试工具、测试进阶-Python编程、Web自动化测试、APP自动化测试、接口自动化测试、测试高级持续集成、测试架构开发测试框架、性能测试、安全测试等。

    学习不要孤军奋战,最好是能抱团取暖,相互成就一起成长,群众效应的效果是非常强大的,大家一起学习,一起打卡,会更有学习动力,也更能坚持下去。你可以加入我们的测试技术交流扣扣群:914172719(里面有各种软件测试资源和技术讨论)

    喜欢软件测试的小伙伴们,如果我的博客对你有帮助、如果你喜欢我的博客内容,请 “点赞” “评论” “收藏” 一 键三连哦!

  • 相关阅读:
    【推荐系统】:协同过滤和基于内容过滤概述
    面试面麻了,别再为难软件测试人员了···
    OpenGL之纹理过滤(Texture Filtering)、MipMap方法、纹理坐标
    Scrapy设置代理IP方法(超详细)
    FPGA工程师是否有必要转ASIC设计工程师?哪个前景好?
    linux的磁盘分区与管理
    c++视觉处理---均值滤波
    算法系列三:树表查找、哈希查找
    SpringBoot添加过滤器详解
    el-date-picker 禁用时分秒选择(包括禁用下拉框展示)
  • 原文地址:https://blog.csdn.net/m0_67695717/article/details/125502224