selenium-高級應用


 

1 跨瀏覽器測試

1.1 配置selenium standalone Server

下載地址:http://www.seleniumhq.org/download/

根據環境中selenium版本號(1.python 2.help(selenium)),選擇對應版本的selenium-server-standalone-xxx.jar。由於運行jar需要有對應的java環境,現在對應的jdk1.8的版本。
1.2 運行selenium standalone Server
通過命令行,切換到selenium server目錄下,執行 java -jar selenium-server-standalone-xxx.jar。即可執行該jar包。

1.3 selenium grid2工作原理

Grid 是用於設計幫助我們進行分布式測試的工具,其整個結構有一個hub主節點和若干個node代理節點組成。hub用來管理各個子節點的注冊和狀態信息,並接收遠程客戶端代碼的請求調用,然后把請求的命令再轉發給代理節點來執行。使用Grid遠程執行測試的代碼與直接調用Selenium Server是一樣的,只是環境啟動的方式不一樣,需要同時啟動一個hub和至少一個node。

1.3.1 啟動主節點

以hub形式啟動Server就是一個Gird Server,可以通過瀏覽器查看Grid控制台的信息。

1.3.2 啟動代理節點

 

1.4 Remote應用

啟動Selenium Server

from selenium.webdriver import Remote
import time

driver = Remote(command_executor='http://localhost:4444/wd/hub',desired_capabilities=
            {'platfrom':'ANY','browserName':'firefox','version':'','javascriptEnabled':True})
driver.get('http://baidu.com')
driver.find_element_by_id('kw').send_keys('remote')
driver.find_element_by_id('su').click()
time.sleep(3)
driver.quit()

通過Remote()可以參數化使用的瀏覽器。

FireFox = {'platform':'ANY', 'browserName':'firefox', 'version':'', 'javascriptEnabled':True, 'marionette':False }
Chrome = {'platform':'ANY', 'browserName':'chrome', 'version':'', 'javascriptEnabled':True }
Opera= {'platform':'ANY', 'browserName':'opera', 'version':'', 'javascriptEnabled':True }
Iphone= {'platform':'MAC', 'browserName':'iPhone', 'version':'', 'javascriptEnabled':True }
Android  = {'platform':'ANDROID', 'browserName':'android', 'version':'', 'javascriptEnabled':True }

1.5 參數化平台及瀏覽器

1.5.1 啟動本地node

先創建list 字典,定義不同的主機ip,端口號及瀏覽器。然后,通過for循環讀取lists字典中的數據作為Remote()的配置信息,從而使腳本在不同的節點及瀏覽器下執行。

from selenium.webdriver import Remote
import time

lists = {'http://localhost:4444/wd/hub':'chrome','http://localhost:5555/wd/hub':'firefox'}
for host,browser in lists.items():
    print (host,browser)
    driver = Remote(command_executor=host,desired_capabilities={'platform': 'ANY','browserName': browser,'version': '','javascriptEnabled': True})
    driver.get('http://www.baidu.com')
    driver.find_element_by_id('kw').send_keys('remote')
    driver.find_element_by_id('su').click()
    time.sleep(3)
    driver.quit()

1.5.2 啟動遠程node

  1、本地hub主機與遠程node主機之間可以用ping命令連通;
  2、遠程主機必須安裝用例執行的瀏覽器驅動,並且驅動要放到環境變量path的目錄下;
  3、遠程主機必須安裝java環境,因為需要運行Selenium  Server。

 步驟:

       啟動本地hub主機,查看主機ip:
              java -jar selenium-server-standalone-2.48.2.jar -role hub
       啟動遠程主機,查看ip:
         java -jar selenium-server-standalone-2.48.2.jar -role node -port 5555 -hub http://hup主機ip:4444/grid/register
         lists = {'http://localhost:4444/wd/hub':'chrome','http://node主機ip:5555/wd/hub':'firefox'}
1.5.3 雲端測試
借助Sauce Labs做兼容性測試。
 
1.6 threading
from selenium.webdriver import Remote
from threading import Thread
import time

lists = {'http://localhost:4444/wd/hub':'chrome','http://localhost:5555/wd/hub':'firefox'}

def WebTest(host,browser):
    driver = Remote(command_executor=host,
                    desired_capabilities={'platform': 'ANY', 'browserName': browser, 'version': '',
                                          'javascriptEnabled': True})
    driver.get('http://www.baidu.com')
    driver.find_element_by_id('kw').send_keys('remote')
    driver.find_element_by_id('su').click()
    time.sleep(3)
    driver.quit()

if __name__ == '__main__':
    threads=[]     
#創建線程
for host, browser in lists.items(): print(host, browser) t = Thread(target=WebTest,args=(host,browser)) threads.append(t)
#啟動線程
for thr in threads: thr.start() print(time.strftime('%Y%m%d%H%M%S'))

2 數據驅動測試與Page Object

使用Python下的數據驅動模式(ddt)庫,結合unittest庫以數據驅動模式創建百度搜索的測試。

pip命令進行下載並安裝:pip install ddt

2.1 一個簡單的數據驅動測試

為了創建數據驅動測試,需要在測試類上使用@ddt裝飾符,在測試方法上使用@data裝飾符。@data裝飾符把參數當作測試數據,參數可以是單個值、列表、元組、字典。對於列表,需要用@unpack裝飾符把元組和列表解析成多個參數。

import unittest,time
from selenium import webdriver
from ddt import ddt,data,unpack

@ddt
class WebTest(unittest.TestCase):
    @classmethod
    def setUpClass(cls):
        cls.driver = webdriver.Firefox()
        cls.driver.implicitly_wait(3)
        cls.driver.get("http://baidu.com")

    @classmethod
    def tearDownClass(cls):
        cls.driver.quit()

    @data(("圖書館","圖書館_百度搜索"),("博客","博客_百度搜索"))
    @unpack
     # 搜索
    def test_search_info(self,search_value, expected_result):
        self.search = self.driver.find_element_by_xpath("//*[@id='kw']")
        self.search.clear()
        self.search.send_keys(search_value)
        self.search.submit()
        time.sleep(1.5)
        self.result = self.driver.title
        self.assertEqual(expected_result,self.result)

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

在test_search()方法中,search_value與expected_result兩個參數用來接收元組解析的數據。當運行腳本時,ddt把測試數據轉換為有效的python標識符,生成名稱為更有意義的測試方法。結果如下:

2.2 使用外部數據的數據驅動測試

2.2.1 通過CSV獲取數據

同上在@data裝飾符使用解析外部的CSV(testdata.csv)來作為測試數據(代替之前的測試數據)。其中數據如下:

接下來,先要創建一個get_data()方法,其中包括路徑(這里默認使用當前路徑)、CSV文件名。調用CSV庫去讀取文件並返回一行數據。再使用@ddt及@data實現外部數據驅動測試百度搜索,代碼如下:

import csv,unittest,time
from selenium import webdriver
from ddt import ddt,data,unpack

def GetData(filename):
    # create an empty list to store rows
    rows = []
    # open the CSV file
    data_file = open(filename, "r",encoding='utf-8')
    # create a CSV Reader from CSV file
    reader = csv.reader(data_file)
    # skip the headers
    next(reader, None)
    # add rows from reader to list
    for row in reader:
        rows.append(row)
    return rows

@ddt
class WebTest(unittest.TestCase):
    @classmethod
    def setUpClass(cls):
        cls.driver = webdriver.Firefox()
        cls.driver.implicitly_wait(3)
        cls.driver.get("http://baidu.com")

    @classmethod
    def tearDownClass(cls):
        cls.driver.quit()
@data(
*GetData("testdata.csv")) #@data(*GetData(dir+'\\_test'+'\\testdata.csv')),dir=os.path.dirname(os.path.abspath(__file__)) @unpack # 搜索 def test_search_info(self,search_value, expected_result): self.search = self.driver.find_element_by_xpath("//*[@id='kw']") self.search.clear() self.search.send_keys(search_value) self.search.submit() time.sleep(1.5) self.result = self.driver.title self.assertEqual(expected_result,self.result) if __name__ == '__main__': unittest.main(verbosity=2)

測試執行時,@data將調用get_data()方法讀取外部數據文件,並將數據逐行返回給@data。執行的結果如下:

2.2.2 通過excel獲取數據

讀取excel文件需要用到xlrd的庫,安裝命令:pip install xlrd

創建excel文件如圖:

import xlrd,unittest,time
from selenium import webdriver
from ddt import ddt,data,unpack
import os,sys

def GetData(filename):
    # create an empty list to store rows
    rows = []
    data_file = xlrd.open_workbook(filename,encoding_override='utf-8')
    sheet = data_file.sheet_by_index(0) #通過索引順序獲取
    for row_idx in range(1,sheet.nrows): #從第1行開始獲取
        rows.append(list(sheet.row_values(row_idx,0,sheet.ncols))) #從第0列開始獲取
    print(rows)
    return rows

@ddt
class WebTest(unittest.TestCase):
    @classmethod
    def setUpClass(cls):
        cls.driver = webdriver.Firefox()
        cls.driver.implicitly_wait(3)
        cls.driver.get("http://baidu.com")

    @classmethod
    def tearDownClass(cls):
        cls.driver.quit()
    dir = os.path.dirname(os.path.abspath(__file__))

    @data(*GetData("testdata.xlsx"))
    @unpack
     # 搜索
    def test_search_info(self,search_value, expected_result):
        self.search = self.driver.find_element_by_xpath("//*[@id='kw']")
        self.search.clear()
        self.search.send_keys(search_value)
        self.search.submit()
        time.sleep(1.5)
        self.result = self.driver.title
        self.assertEqual(expected_result,self.result)

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

 如果想從數據庫的庫表中獲取數據,同樣也需要一個get_data()方法,並且通過DB相關的庫來連接數據庫、SQL查詢來獲取測試數據。

3 Page Object設計模式

Page Object模式,創建一個對象來對應頁面的一個應用。故我們可以為每個頁面定義一個類,並為每個頁面的屬性和操作構建模型。體現在對界面交互細節的封裝,測試在更上層使用頁面對象,在底層的屬性或者操作的更改不會中斷測試。減少代碼重復,提高測試代碼的可讀性和可維護性。

from selenium import webdriver
from selenium.webdriver.common.by import By
from time import sleep

#創建基礎類
class BasePage(object):
    #初始化
    def __init__(self, driver):
        self.base_url = 'https://mail.qq.com/'
        self.driver = driver
        self.timeout = 30

    #定義打開登錄頁面方法
    def _open(self):
        url = self.base_url
        self.driver.get(url)
        self.driver.switch_to.frame('login_frame')  #切換到登錄窗口的iframe

  #定義定義open方法,調用_open()進行打開
    def open(self):
        self._open()

    #定位方法封裝
    def find_element(self,*loc):
        return self.driver.find_element(*loc)

#創建LoginPage類
class LoginPage(BasePage):
    username_loc = (By.ID, "u")
    password_loc = (By.ID, "p")
    login_loc = (By.ID, "login_button")

    #輸入用戶名
    def type_username(self,username):
        self.find_element(*self.username_loc).clear()
        self.find_element(*self.username_loc).send_keys(username)

    #輸入密碼
    def type_password(self,password):
        self.find_element(*self.password_loc).send_keys(password)

    #點擊登錄
    def type_login(self):
        self.find_element(*self.login_loc).click()

#創建test_user_login()函數
def test_user_login(driver, username, password):
    """測試用戶名/密碼是否可以登錄"""
    login_page = LoginPage(driver)
    login_page.open()
    login_page.type_username(username)
    login_page.type_password(password)
    login_page.type_login()

#創建main()函數
def main():
    driver = webdriver.Edge()
    username = '3494xxxxx'    #qq號碼
    password = 'kemixxxx'    #qq密碼
    test_user_login(driver, username, password)
    sleep(3)

    driver.quit()

if __name__ == '__main__':
    main()

       首先創建一個基礎BasePage類,在初始化方法__init__()中定義驅動(driver),基本的URL(base_url)和超時時間(timeout)等。定義open()方法用於打開URL,這里是由_open()方法來實現,而find_element()方法用於元素定位。

  接下來的BasePage類中定義的方法都是頁面操作的基本方法。LoginPage類並繼承BasePage類,這也是Page Object設計模式中最重要的對象層。LoginPage類中主要對登錄頁面上元素進行封裝,使其成為具體的操作方法。如對用戶名、密碼框和登錄按鈕都封裝成方法。

  然后定義test_user_login()函數將單個元素操作組成一個完整的動作,包含打開瀏覽器、輸入用戶名、密碼並點擊登錄按鈕等。使用時將driver、username、password作為函數的入參,這樣的函數具有很強的可重用性。

  最后使用main()函數進行用戶操作行為,現在只關心用哪個瀏覽器、登錄的用戶名和密碼是什么,至少輸入框、按鈕是如何定位的,則不關心。即實現了不同層關心不同問題。如果有多個用戶名/密碼需要登錄,那么只用改寫main()方法的參數即可。

5 自動化測試框架

Test_framework
    |--config(配置文件)
    |--data(數據文件)
    |--drivers(驅動)
    |--log(日志)
    |--report(報告)
    |--test(測試用例)
    |--utils(公共方法)
    |--ReadMe.md(加個說明性的文件,告訴團隊成員框架需要的環境以及用法)

把配置抽出來放到config.yml中:

URL: http://www.baidu.com

為了讀取yaml文件,需要一個封裝YamlReader類,在utils中創建file_reader.py文件:

import yaml
import os

class YamlReader:
    def __init__(self, yamlf):
        if os.path.exists(yamlf):
            self.yamlf = yamlf
        else:
            raise FileNotFoundError('文件不存在!')
        self._data = None

    @property
    def data(self):
        # 如果是第一次調用data,讀取yaml文檔,否則直接返回之前保存的數據
        if not self._data:
            with open(self.yamlf, 'rb') as f:
                self._data = list(yaml.safe_load_all(f))  # load后是個generator,用list組織成列表
        return self._data

需要一個Config類來讀取配置,config.py:

"""
讀取配置。這里配置文件用的yaml,也可用其他如XML,INI等,需在file_reader中添加相應的Reader進行處理。
"""
import os

from utils.file_reader import YamlReader

# 通過當前文件的絕對路徑,其父級目錄一定是框架的base目錄,然后確定各層的絕對路徑。如果你的結構不同,可自行修改。
# 之前直接拼接的路徑,修改了一下,用現在下面這種方法,可以支持linux和windows等不同的平台,使用os.path.split()和os.path.join(),不要直接+'\\xxx\\ss'這樣
BASE_PATH = os.path.split(os.path.dirname(os.path.abspath(__file__)))[0]  #os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
CONFIG_FILE = os.path.join(BASE_PATH, 'config', 'config.yaml')
DATA_PATH = os.path.join(BASE_PATH, 'data')
DRIVER_PATH = os.path.join(BASE_PATH, 'drivers')
LOG_PATH = os.path.join(BASE_PATH, 'log')
REPORT_PATH = os.path.join(BASE_PATH, 'report')

class Config:
    def __init__(self, config=CONFIG_FILE):
        self.config = YamlReader(config).data

    def get(self, element, index=0):
        """
        yaml是可以通過'---'分節的。用YamlReader讀取返回的是一個list,第一項是默認的節,如果有多個節,可以傳入index來獲取。
        把框架相關的配置放在默認節,其他的關於項目的配置放在其他節中。可以在框架中實現多個項目的測試。
        """
        return self.config[index].get(element)

test.py

import time,os,sys
import unittest
from selenium import webdriver
from selenium.webdriver.common.by import  By
PATH = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.append(PATH)
from utils.config import Config

class TestBaiDu(unittest.TestCase):
    URL = Config().get('URL')
    locator_kw = (By.ID, 'kw')
    locator_su = (By.ID, 'su')
    locator_result = (By.XPATH, '//div[contains(@class, "result")]/h3/a')

    def setUp(self):
        self.driver = webdriver.Firefox()
        self.driver.get(self.URL)

    def tearDown(self):
        self.driver.quit()

    def test_search_0(self):
        self.driver.find_element(*self.locator_kw).send_keys('防彈少年團')
        self.driver.find_element(*self.locator_su).click()
        time.sleep(2)
        links = self.driver.find_elements(*self.locator_result)
        for link in links:
            print(link.text)

    def test_search_1(self):
        self.driver.find_element(*self.locator_kw).send_keys('復仇者聯盟')
        self.driver.find_element(*self.locator_su).click()
        time.sleep(2)
        links = self.driver.find_elements(*self.locator_result)
        for link in links:
            print(link.text)

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

在utils中創建一個log.py文件,Python有很方便的logging庫,對其進行簡單的封裝,使框架可以很簡單地打印日志(輸出到控制台以及日志文件)。

import os,sys
import logging
from logging.handlers import TimedRotatingFileHandler
PATH = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.append(PATH)
from utils.config import LOG_PATH


class Logger(object):
    def __init__(self, logger_name='framework'):
        self.logger = logging.getLogger(logger_name)
        logging.root.setLevel(logging.NOTSET)
        self.log_file_name = 'test.log'
        self.backup_count = 5
        # 日志輸出級別
        self.console_output_level = 'WARNING'
        self.file_output_level = 'DEBUG'
        # 日志輸出格式
        self.formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')

    def get_logger(self):
        """在logger中添加日志句柄並返回,如果logger已有句柄,則直接返回"""
        if not self.logger.handlers:  # 避免重復日志
            console_handler = logging.StreamHandler()
            console_handler.setFormatter(self.formatter)
            console_handler.setLevel(self.console_output_level)
            self.logger.addHandler(console_handler)

            # 每天重新創建一個日志文件,最多保留backup_count份
            file_handler = TimedRotatingFileHandler(filename=os.path.join(LOG_PATH, self.log_file_name),
                                                    when='D',
                                                    interval=1,
                                                    backupCount=self.backup_count,
                                                    delay=True,
                                                    encoding='utf-8'
                                                    )
            file_handler.setFormatter(self.formatter)
            file_handler.setLevel(self.file_output_level)
            self.logger.addHandler(file_handler)
        return self.logger

logger = Logger().get_logger()

test.py

import time,os,sys
import unittest
from selenium import webdriver
from selenium.webdriver.common.by import  By
PATH = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.append(PATH)
from utils.config import Config
from utils.log import logger

class TestBaiDu(unittest.TestCase):
    URL = Config().get('URL')
    locator_kw = (By.ID, 'kw')
    locator_su = (By.ID, 'su')
    locator_result = (By.XPATH, '//div[contains(@class, "result")]/h3/a')

    def setUp(self):
        self.driver = webdriver.Firefox()
        self.driver.get(self.URL)

    def tearDown(self):
        self.driver.quit()

    def test_search_0(self):
        self.driver.find_element(*self.locator_kw).send_keys('防彈少年團')
        self.driver.find_element(*self.locator_su).click()
        time.sleep(2)
        links = self.driver.find_elements(*self.locator_result)
        for link in links:
            logger.info(link.text)

    def test_search_1(self):
        self.driver.find_element(*self.locator_kw).send_keys('復仇者聯盟')
        self.driver.find_element(*self.locator_su).click()
        time.sleep(2)
        links = self.driver.find_elements(*self.locator_result)
        for link in links:
            logger.info(link.text)

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

執行test.py,打印的信息都輸出到log文件夾的test.log文件。

可以把log的設置放到config中,修改config.yml

URL: http://www.baidu.com
log:
    file_name: test.log
    backup: 5
    console_level: WARNING
    file_level: DEBUG
    pattern: '%(asctime)s - %(name)s - %(levelname)s - %(message)s'

log.py文件

import os,sys
import logging
from logging.handlers import TimedRotatingFileHandler
PATH = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.append(PATH)
from utils.config import LOG_PATH,Config


class Logger(object):
    def __init__(self, logger_name='framework'):
        self.logger = logging.getLogger(logger_name)
        logging.root.setLevel(logging.NOTSET)
        c = Config().get('log')
        self.log_file_name = c.get('file_name') if c and c.get('file_name') else 'test.log'  # 日志文件
        self.backup_count = c.get('backup') if c and c.get('backup') else 5  # 保留的日志數量
        # 日志輸出級別
        self.console_output_level = c.get('console_level') if c and c.get('console_level') else 'WARNING'
        self.file_output_level = c.get('file_level') if c and c.get('file_level') else 'DEBUG'
        # 日志輸出格式
        pattern = c.get('pattern') if c and c.get('pattern') else '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
        self.formatter = logging.Formatter(pattern)

    def get_logger(self):
        """在logger中添加日志句柄並返回,如果logger已有句柄,則直接返回"""
        if not self.logger.handlers:  # 避免重復日志
            console_handler = logging.StreamHandler()
            console_handler.setFormatter(self.formatter)
            console_handler.setLevel(self.console_output_level)
            self.logger.addHandler(console_handler)

            # 每天重新創建一個日志文件,最多保留backup_count份
            file_handler = TimedRotatingFileHandler(filename=os.path.join(LOG_PATH, self.log_file_name),
                                                    when='D',
                                                    interval=1,
                                                    backupCount=self.backup_count,
                                                    delay=True,
                                                    encoding='utf-8'
                                                    )
            file_handler.setFormatter(self.formatter)
            file_handler.setLevel(self.file_output_level)
            self.logger.addHandler(file_handler)
        return self.logger

logger = Logger().get_logger()

修改file_reader.py文件,添加ExcelReader類,實現讀取excel內容的功能:

import yaml
import os
from xlrd import  open_workbook

class YamlReader:
    def __init__(self, yamlf):
        if os.path.exists(yamlf):
            self.yamlf = yamlf
        else:
            raise FileNotFoundError('文件不存在!')
        self._data = None

    @property
    def data(self):
        # 如果是第一次調用data,讀取yaml文檔,否則直接返回之前保存的數據
        if not self._data:
            with open(self.yamlf, 'rb') as f:
                self._data = list(yaml.safe_load_all(f))  # load后是個generator,用list組織成列表
        return self._data

class SheetTypeError(Exception):
    pass

class  ExcelReader:
    def __init__(self, excel, sheet=0, title_line=True):
        if os.path.exists(excel):
            self.excel = excel
        else:
            raise FileNotFoundError('文件不存在!')
        self.sheet = sheet
        self.title_line = title_line
        self._data = list()

    @property
    def data(self):
        if not self._data:
            workbook = open_workbook(self.excel)
            if type(self.sheet) not in [int, str]:
                raise SheetTypeError('Please pass in <type int> or <type str>, not {0}'.format(type(self.sheet)))
            elif type(self.sheet) == int:
                s = workbook.sheet_by_index(self.sheet)
            else:
                s = workbook.sheet_by_name(self.sheet)

            if self.title_line:
                title = s.row_values(0)  # 首行為title
                for col in range(1, s.nrows):
                    # 依次遍歷其余行,與首行組成dict,拼到self._data中
                    self._data.append(dict(zip(title, s.row_values(col))))
            else:
                for col in range(0, s.nrows):
                    # 遍歷所有行,拼到self._data中
                    self._data.append(s.row_values(col))
        return self._data

'''
內部測試
'''
if __name__ == '__main__':
    y = 'C:\\Users\\zhouxy\\PycharmProjects\\untitled\\TestFramework\\config\\config.yaml'
    reader = YamlReader(y)
    print(reader.data)

    e = 'C:\\Users\\zhouxy\\PycharmProjects\\untitled\\TestFramework\\data\\data.xlsx'
    reader = ExcelReader(e, title_line=True)
    print(reader.data)

test.py

import time,os,sys
import unittest
from selenium import webdriver
from selenium.webdriver.common.by import  By
PATH = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.append(PATH)
from utils.config import Config,DATA_PATH
from utils.log import logger
from utils.file_reader import ExcelReader

class TestBaiDu(unittest.TestCase):
    URL = Config().get('URL')
    excel = DATA_PATH + '//data.xlsx'
    locator_kw = (By.ID, 'kw')
    locator_su = (By.ID, 'su')
    locator_result = (By.XPATH, '//div[contains(@class, "result")]/h3/a')

    def setUp(self):
        self.driver = webdriver.Firefox()
        self.driver.get(self.URL)

    def tearDown(self):
        self.driver.quit()

    def test_search(self):
        datas = ExcelReader(self.excel).data
        for d in datas:
            with self.subTest(data=d):
                self.setUp()
                self.driver.find_element(*self.locator_kw).send_keys(d['search'])
                self.driver.find_element(*self.locator_su).click()
                time.sleep(2)
                links = self.driver.find_elements(*self.locator_result)
                for link in links:
                    logger.info(link.text)
                self.tearDown()

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

test目錄再次進行分層,創建page、common、case、suite四個目錄:

test
    |--case(用例文件)
    |--common(跟項目、頁面無關的封裝)
    |--page(頁面)
    |--suite(測試套件,用來組織用例)

結合PageObject進行封裝。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM