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